# 5班進捗シート
# day2(10/1)
とりあえずインクリメントを先輩のを真似して実装してみることに
https://qiita.com/DOSS_INCREMENT/items/8b050847145f635211e2
mainから辿っていく
main->Py_BytesMain->pymain_main->Py_RunMain->pymain_run_python->pymain_run_file(main.c:309)->
main.c:373 PyRun_AnyFileExFlags(6)->
PyRun_SimpleFileExFlags(pythonrun.c:L341)->
pythonrun.c:398 PyRun_FileExFlags(fp, filename, Py_file_input, d, d,closeit,flags);(pythonrun.c:1038)->
pythonrun.c:1054 PyParser_ASTFromFileObject(fp, filename, NULL, start, NULL, NULL,flags, NULL, arena);
おそらくこの中を見ればいいはず。というところまでわかった。
# day2.5
## デバッグのメモ
uftraceでどんな関数が呼び出されてるかみてみるといいかも
http://namopa.hatenablog.com/entry/2017/12/13/174725
http://namopa.hatenablog.com/entry/2017/12/13/175106
uftraceがうまくいかなかったらlibitrace
https://doss-gitlab.eidos.ic.i.u-tokyo.ac.jp/tau/libitrace
## その他参考にしたもの
[python3.7でインクリメント](https://hackernoon.com/modifying-the-python-language-in-7-minutes-b94b0a99ce14)
[これもcpythonの改造](https://yigarashi.hatenablog.com/entry/cpython)
ドキュメントたち
https://devguide.python.org/grammar/
https://pythonextensionpatterns.readthedocs.io/en/latest/refcount.html
https://prog-lang-sys-ja-slack.github.io/wiki/
↑こんなのに参加してみてもいいかもしれない
## インクリメントの実装(準備運動)
branchを切るのを忘れずに.
## python.gram
python.gramの文法が全くわからない…
```
Grammar/python.gram: The grammar, with actions that build AST nodes. After changing it, run make regen-pegen, to regenerate Parser/parser.c. (This runs Python’s parser generator, Tools/peg_generator).
```
って書いてあるのでとりあえず試してみたけど,make regen-pegenが通らない(future feature annotations is not definedが出る)
->python3.7でやる それでもsyntax error
->セイウチ演算子が使われているので3.8以上が必要.できた.ただしこれをやるとgnome-terminal(ubuntuの端末)が起動しなくなるのでやめましょう。…
多分pyenvでバージョン管理してやるとか色々できると思いますがわからなかったのでお願いします
Grammer/python.gram :455(factorのところ)
```
| '++' a=factor {_Py_BinOp(a,Add,a,EXTRA)}
```
Grammer/Tokens:38(良さげなところに追加)
```
INCREMENT '++'
```
```bash
make regen-token
make regen-pegen
```
たしかにparser.cとtoken.cとtoken.hが変更された。
これを`make`した結果はこれ

二倍を返す特に役に立たない演算子ができた.
## インクリメントの実装
じゃあこれをインクリメントにしていく。
```
| '++' a=factor { _Py_UnaryOp(UInc, a, EXTRA)}
```
にpython.gramを変更.
お仲間のUAddとかがどこで定義されているか調べたい。とりあえず`make regen-pegen`あとに`make`してみる。
parser.cで`error:UInc undeclared`が出る。
Parser/Python.asdl
```
unaryop = Invert | Not | UAdd | USub | UInc
```
```
make regen-ast
```
Include/Python-ast.hとPython/Python-ast.cが再生成される.
この状態で`make`と`make install`できるが(install中にlibinstallがfailedするようになる、後述),pythonを実行すると`SystemError: unary op 5 not be possible`が出る。
UAddが他にどういうところで出てくるかを調べる.compileが怪しい?
Python/compile.c
```c
case UNARY_POSITIVE:
case UNARY_NEGATIVE:
case UNARY_NOT:
case UNARY_INVERT:
case UNARY_INCREMENT:
```
```c
case Usub:
return UNARY_NEGATIVE;
case UInc:
return UNARY_INCREMENT;
```
makeすると`'UNARY_INCREMENT' undeclared`が出る。それはそう。
Incrude/opcode.h
```
#define UNARY_INCREMENT 166
```
python3を実行すると`Python/compile.c:1157 compiler_addop: Assertion '!HAS_ARG(opcode)' failed`が出る。
原因を調べると,以下のようにすれば良さそうなことがわかる.
Incrude/opcode.h
```c
#define UNARY_INCREMENT 13
```
もう一度python3を実行する.
```
XXX lineno: 1, opcode: 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: unknown opcode
```
(先輩のブログより抜粋)それにしたってopcode.hでオペコードを定義したのにunknownとはどういうことか」と思ってもう一度opcode.hを開いてみる。一行目に
opcode.h
```
/* Auto-generated by Tools/scripts/generate_opcode_h.py */
```
つまり自動生成されているので,
code:python Lib/opcode.py
```python
def_op('UNARY_INCREMENT', 13)
```
とした後.
```
regen-opcode
regen-opcode-targets
```
する.(regen-allでも良い)
あとはブログと同じになってきたのでブログを読んでコピペ
Python/ceval.c
```c
TARGET(UNARY_INCREMENT) {
PyObject *right = TOP();
PyObject *inv, *sum;
// note that -(~x) == x+1 for all x
inv = PyNumber_Invert(right);
//Py_DECREF(left);
if (inv == NULL)
goto error;
sum = PyNumber_Negative(inv);
Py_DECREF(inv);
if (sum == NULL)
goto error;
SET_TOP(sum);
DISPATCH();
}
```
あとはstoreを追加するだけ.
先輩がやっていた手法がよく理解できなかったのでとりあえずカスみたいな実装をしといた.
STORE_NAMEを実行していることが`./bin/python3 -m dis <filename>`からわかるので、TARGET(STORE NAME)を真似
Python/ceval.c
```
case TARGET(UNARY_INCREMENT): {
PyObject *right = TOP();
PyObject *inv,*sum;
//-(~x)=x+1
inv=PyNumber_Invert(right);
if (inv == NULL)
goto error;
sum = PyNumber_Negative(inv);
Py_DECREF(inv);
if (sum == NULL)
goto error;
//DISPATCH();
PyObject *names = f->f_code->co_names;
PyObject *name = GETITEM(names, oparg);
// PyObject *name = right;
PyObject *v = sum;
PyObject *ns = f->f_locals;
int err;
if (ns == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals found when storing %R", name);
Py_DECREF(v);
goto error;
}
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);
Py_DECREF(v);
if (err != 0)
goto error;
Py_DECREF(right);
SET_TOP(sum);
DISPATCH();
}
```
できた

できた!!!!!なんとインクリメントの実装に(だいたい)成功しました。
が、なんか挙動が不自然。最後のところでtestは4,hogeは1にならなければいけません。たぶんceval.cのせい。めんどくさいので(day3まで)直しません。
よく使うこういうのは大丈夫そう

## チェックリストにあるのにやっていないこと
[o]Python/ast.c will need changes to validate AST objects involved with the grammar change.
## 感想
練習としてやろうということでしたが,先輩たちのブログが有能すぎて重要なところはコピペでできてしまったのであまり練習になりませんでした感がある
## 詰まったこと
python3.6.9だと__future__構文が使えなくてParserを作成するためのmakeに失敗した
# day3
day2.5の確認
# day4
## gitlab
gitlabの設定を完了した
## やること決めた
- 三項演算子をC言語風にする
-
- switchをつくる
## インクリメントでエラーが出ることを確認
### setupterm
python.gram, Python.asdlを書き換えた後make clean -> make regen-all -> make とすると途中で下のようなメッセージ。
```
/home/denjo/doss/cpython/Modules/_cursesmodule.c: In function ‘_curses_setupterm_impl’:
/home/denjo/doss/cpython/Modules/_cursesmodule.c:3362:35: error: implicit declaration of function ‘setupterm’; did you mean ‘set_term’? [-Werror=implicit-function-declaration]
if (!initialised_setupterm && setupterm((char *)term, fd, &err) == ERR) {
^~~~~~~~~
set_term
cc1: some warnings being treated as errors
building '_curses_panel' extension
```
### make installが止まる
make installするとtestのあたりで以下のように止まってしまった。libinstall で失敗している。
```
Compiling '/home/denjo/doss/cpython_install/lib/python3.10/test/test_winreg.py'...
Exception in thread Thread-1:
Traceback (most recent call last):
File "/home/denjo/doss/cpython_install/lib/python3.10/threading.py", line 960, in _bootstrap_inner
Compiling '/home/denjo/doss/cpython_install/lib/python3.10/test/test_winsound.py'...
self.run()
File "/home/denjo/doss/cpython_install/lib/python3.10/concurrent/futures/process.py", line 320, in run
self.terminate_broken(cause)
File "/home/denjo/doss/cpython_install/lib/python3.10/concurrent/futures/process.py", line 455, in terminate_broken
work_item.future.set_exception(bpe)
File "/home/denjo/doss/cpython_install/lib/python3.10/concurrent/futures/_base.py", line 540, in set_exception
raise InvalidStateError('{}: {!r}'.format(self._state, self))
concurrent.futures._base.InvalidStateError: CANCELLED: <Future at 0x7ff8e97f03c0 state=cancelled>
Traceback (most recent call last):
File "/home/denjo/doss/cpython_install/lib/python3.10/compileall.py", line 460, in <module>
exit_status = int(not main())
File "/home/denjo/doss/cpython_install/lib/python3.10/compileall.py", line 437, in main
Compiling '/home/denjo/doss/cpython_install/lib/python3.10/test/test_with.py'...
if not compile_dir(dest, maxlevels, args.ddir,
File "/home/denjo/doss/cpython_install/lib/python3.10/compileall.py", line 112, in compile_dir
success = min(results, default=True)
File "/home/denjo/doss/cpython_install/lib/python3.10/concurrent/futures/process.py", line 559, in _chain_from_iterable_of_lists
for element in iterable:
File "/home/denjo/doss/cpython_install/lib/python3.10/concurrent/futures/_base.py", line 600, in result_iterator
yield fs.pop().result()
File "/home/denjo/doss/cpython_install/lib/python3.10/concurrent/futures/_base.py", line 440, in result
Compiling '/home/denjo/doss/cpython_install/lib/python3.10/test/test_xdrlib.py'...
return self.__get_result()
File "/home/denjo/doss/cpython_install/lib/python3.10/concurrent/futures/_base.py", line 389, in __get_result
raise self._exception
concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending.
Compiling '/home/denjo/doss/cpython_install/lib/python3.10/test/test_wsgiref.py'...
KeyboardInterrupt
Makefile:1451: recipe for target 'libinstall' failed
make: [libinstall] 割り込み (無視されました)
```
UAddはあってUIncがないファイルを探したいので以下でにらめっこ。Python/ast_unparse.cが見つかる。どれが自動生成されるファイルなのかがはっきりしなくて苦労。
```
~/doss/cpython$ grep UAdd -r ./
~/doss/cpython$ grep UInc -r ./
```
Python/ast_unparse.c :179でUnaryOpのpriorityを定義しているので
```
case UInc: op = "++";pr = PR_FACTOR; break;
```
を追加したところ解決。(priorityが適切かは怪しい...)
### `hoge+ ++a`でhogeが++aになる
前述の通り。
## `if ~ then ... else xxx`
ブランチを切るのをわすれずに
```
git checkout master
git checkout -b ternary
```
とりあえずインクリメントにはバグがたくさん存在するので、インクリメントを実装する前の段階から実装しています(私は)
これでmake,make installすると止まらないでmake installが完了する
構文木がどうなるのか全然わからん
とりあえず似ている構文について見てみる?
`... if ~ else xxx`について見てみる

↑ceval.cの中でどこを使ってるのかわかる
条件を先に評価して格納し、POP_JUMP_IF_FALSEをセットして、結果を読み出し、その結果でJUMP_FORWARDしてそう。
POP_JUMP_IF_FALSE のceval.c:3187が使われてそう
POP_JUMP_IF_FALSEを何回か遡るとCALL_FUNCTIONが出てくる?
よくわからないのでpython.gramを見てみる
Grammer/python.gram
```
expressions[expr_ty]:
| a=expression b=(',' c=expression { c })+ [','] {
_Py_Tuple(CHECK(_PyPegen_seq_insert_in_front(p, a, b)), Load, EXTRA) }
| a=expression ',' { _Py_Tuple(CHECK(_PyPegen_singleton_seq(p, a)), Load, EXTRA) }
| expression
expression[expr_ty] (memo):
| a=disjunction 'if' b=disjunction 'else' c=expression { _Py_IfExp(b, a, c, EXTRA) }
| disjunction
| lambdef
```
あたりが三項演算子なので、
```
| 'if' a=disjunction 'then' b=disjunction 'else' c=expression { _Py_IfExp(a, b, c, EXTRA) }
```
`make regen-pegen`

できちゃった....
### 考えるべきこと
disjunctionとexpressionsの使い分け
## switchを作る
if else Grammer/python.gram(157)
```
if_stmt[stmt_ty]:
| 'if' a=named_expression ':' b=block c=elif_stmt { _Py_If(a, b, CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, c)), EXTRA) }
| 'if' a=named_expression ':' b=block c=[else_block] { _Py_If(a, b, c, EXTRA) }
elif_stmt[stmt_ty]:
| 'elif' a=named_expression ':' b=block c=elif_stmt { _Py_If(a, b, CHECK(_PyPegen_singleton_seq(p, c)), EXTRA) }
| 'elif' a=named_expression ':' b=block c=[else_block] { _Py_If(a, b, c, EXTRA) }
else_block[asdl_stmt_seq*]: 'else' ':' b=block { b }
```
for Grammer/python.gram(168)
```
for_stmt[stmt_ty]:
| 'for' t=star_targets 'in' ~ ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] {
_Py_For(t, ex, b, el, NEW_TYPE_COMMENT(p, tc), EXTRA) }
| ASYNC 'for' t=star_targets 'in' ~ ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] {
CHECK_VERSION(5, "Async for loops are", _Py_AsyncFor(t, ex, b, el, NEW_TYPE_COMMENT(p, tc), EXTRA)) }
| invalid_for_target
```
が参考にできそう
```
switch_stmt[なにか]
| 'switch' a=(変数) ':' b=(case文のブロック) {_Py_Switch(a,Extra)}
case_block[なにか]
| 'case' a=(定数) ':' b=block {_Py_Case(a,Extra)}
```
# day4.5
### かんがえる
「なにか」はたぶんstmt_tyだけどstmt_tyってなんだ
どうやってcase文がswich文の中で行われていることを知るんだろう
_Py_Caseを錬成するにあたってそれをどこで定義すればいいか
_PyPegen_singleton_seqはparser.cとpegen.cにあった(parser.cはpython.gramから自動生成されるので考えなくて良さそう)
pegen.cを見てみると/* Creates a single-element asdl_seq* that contains a */とある。asdl_seqってなんだ…
_Py_AsyncForはPython-ast.h(Parser/Python.asdlから自動生成される)とparser.cのなかにあった
バイナリコード

caseもLOAD_NAME LOAD_CONST COMARE_OP POP_JUMP_IF_FALSEをつかえばよさそう
### やってみる
```
| &'while' while_stmt
| &'switch' switch_stmt
.......https://i.imgur.com/h7DH8wh.png
else_block[asdl_stmt_seq*]: 'else' ':' b=block { b }
switcha_stmt[stmt_ty]:
| 'switcha' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,b,EXTRA)}
case_block[stmt_ty]:
| 'case' a=disjunction ':' b=block c=[case_block] {_Py_Case(a,b,c,EXTRA)}
```
caseのaはatomかも
つぎはParser/Python.asdlに関数を追加する
```
| For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
| AsyncFor(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
| While(expr test, stmt* body, stmt* orelse)
| If(expr test, stmt* body, stmt* orelse)
| With(withitem* items, stmt* body, string? type_comment)
| AsyncWith(withitem* items, stmt* body, string? type_comment)
```
の下に、
```
| Switch(stmt* body, stmt* cases)
| Case(expr test, stmt* body,stmt* orelse)
```
make regen-all
が通る。warningは以下。
```
Parser/parser.c: In function 'switch_stmt_rule':
Parser/parser.c:3898:33: warning: passing argument 1 of '_Py_Switch' from incompatible pointer type [-Wincompatible-pointer-types]
_res = _Py_Switch ( a , EXTRA );
^
In file included from Parser/pegen.h:7:0,
from Parser/parser.c:2:
./Include/Python-ast.h:621:9: note: expected 'asdl_stmt_seq * {aka struct <anonymous> *}' but argument is of type 'expr_ty {aka struct _expr *}'
stmt_ty _Py_Switch(asdl_stmt_seq * body, int lineno, int col_offset, int
```
これはたぶんSwitch(expr test)にすることで解決する
```
Python/compile.c: In function 'compiler_visit_stmt':
Python/compile.c:3381:5: warning: enumeration value 'Switch_kind' not handled in switch [-Wswitch]
switch (s->kind) {
^~~~~~
Python/compile.c:3381:5: warning: enumeration value 'Case_kind' not handled in switch [-Wswitch]
Python/symtable.c: In function 'symtable_visit_stmt':
Python/symtable.c:1178:5: warning: enumeration value 'Switch_kind' not handled in switch [-Wswitch]
switch (s->kind) {
^~~~~~
Python/symtable.c:1178:5: warning: enumeration value 'Case_kind' not handled in switch [-Wswitch]
```
make make installするとエラーが出る
```
File "/tmp/tmpoyixj5ld/pip-20.1.1-py2.py3-none-any.whl/pip/_internal/vcs/bazaar.py", line 75
def switch(self, dest, url, rev_options):
^
SyntaxError: invalid syntax
```
switchをすべてswitchaにすると消えた(荒治療)
これでsyntax errorにはならないようになった気がするけどなる。なんでだろう。
python3 -dでしらべたところ、NEWLINE関係で詰まってた。
python.gramを
```
switcha_stmt[stmt_ty]:
| 'switcha' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,EXTRA)}
case_block[stmt_ty]:
| 'case' a=disjunction ':' b=block c=[case_block] {_Py_Case(a,b,c,EXTRA)}
```
に変更
これでsyntax errorは出なくなった。
### 中身を作る
今は中身がないので
```
python3: Parser/pegen.c:1146: _PyPegen_run_parser: Assertion `PyAST_Validate(res)' failed.
Aborted
```
が出る状態。これを中身を作って消していく。
たぶんcompile.cをいじればいいはず?
compile.c
```
case Switcha_kind:
return compiler_switch(c, s);
case Case_kind:
return compiler_case(c, s);
```
で、compiler_switch,compiler_caseをcompiler_ifを参考に変えていけば良さそう
```
static int
compiler_switch(struct compiler *c, stmt_ty s)
{
return 1;
}
static int
compiler_case()
{
return 1;
}
```
```
python3: Parser/pegen.c:1146: _PyPegen_run_parser: Assertion `PyAST_Validate(res)' failed.
```
が出る。
ast.c L539
```
case Interactive_kind:
res = validate_stmts(mod->v.Interactive.body);
```
でvalidate_stmtsが0になっていることが問題のようだ。
これをgdbで追うとvalidate_stmtのなかでSwitch_kindとCase_kindがないことが問題そうに見える。
validate_stme内にとりあえず以下のように追加しておく
```
case Switcha_kind:
return 1;
case Case_kind:
return 1;
```
エラーが出なくなった。
ただしcaseはなにもしないまま。

### compiler_xxってどんなふうにできてる?
```
static int
compiler_if(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
int constant;
assert(s->kind == If_kind);
end = compiler_new_block(c);
if (end == NULL)
return 0;
constant = expr_constant(s->v.If.test);
/* constant = 0: "if 0"
* constant = 1: "if 1", "if 2", ...
* constant = -1: rest */
if (constant == 0) {
BEGIN_DO_NOT_EMIT_BYTECODE
VISIT_SEQ(c, stmt, s->v.If.body);
END_DO_NOT_EMIT_BYTECODE
if (s->v.If.orelse) {
VISIT_SEQ(c, stmt, s->v.If.orelse);
}
} else if (constant == 1) {
VISIT_SEQ(c, stmt, s->v.If.body);
if (s->v.If.orelse) {
BEGIN_DO_NOT_EMIT_BYTECODE
VISIT_SEQ(c, stmt, s->v.If.orelse);
END_DO_NOT_EMIT_BYTECODE
}
} else {
if (asdl_seq_LEN(s->v.If.orelse)) {
next = compiler_new_block(c);
if (next == NULL)
return 0;
}
else {
next = end;
}
if (!compiler_jump_if(c, s->v.If.test, next, 0)) {
return 0;
}
VISIT_SEQ(c, stmt, s->v.If.body);
if (asdl_seq_LEN(s->v.If.orelse)) {
ADDOP_JUMP(c, JUMP_FORWARD, end);
compiler_use_next_block(c, next);
VISIT_SEQ(c, stmt, s->v.If.orelse);
}
}
compiler_use_next_block(c, end);
return 1;
}
```
ふんふん
`constant = (s->v.Switcha.test == s->v.Case.test):1?0;`的なことがしたいね…
# day5
stmt_ty $\rightarrow$ 何かのルールを決めてる?
Parser/parser.c(3540-)(自動生成)
```
static stmt_ty
if_stmt_rule(Parser *p)
{
D(p->level++);
if (p->error_indicator) {
D(p->level--);
return NULL;
}
stmt_ty _res = NULL;
int _mark = p->mark;
if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
p->error_indicator = 1;
D(p->level--);
return NULL;
}
```
~_kindはPython/compile.cのcompiler_visit_stmtとfind_annでついあしないとだめ?
### きょうのまとめ(押川)
ガワだけ作ったけどこれあんまり意味がなかったかもしれない
_PyPegen_xxxにしないといけないかもしれない
(いとう)
条件分岐の構造的にtry文が参考になるかなとpython.gram, python.asdlあたりを読もうとするもよくわからず。エラーハンドラとかは関係ないので流して大枠を捉えられたら...😇
(押川)
まずswitchが後続ブロックとしてなんらかのstmt*をうけとるためにはstmt_tyじゃなくてasdl_stmt_seq*じゃないといけないと思うんだけど、case文は機能的にどう頑張ってもstmt_ty(関数とか加えられる)になってしまう気がする
# day5.5
とりあえず以下でがんばってみる
python.asdl
```
| Switcha(expr test, stmt* cases)
| Case(expr test, stmt* body,stmt* orelse)
```
python.gram
```
switcha_stmt[stmt_ty]:
| 'switcha' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,b,EXTRA)}
case_block[stmt_ty]:
| 'case' a=atom ':' b=block c=[case_block] {_Py_Case(a,b,c,EXTRA)}
```
しばらく頑張ってみたが、
```
python3: Python/compile.c:3628: compiler_nameop: Assertion `scope || PyUnicode_READ_CHAR(name, 0) == '_'' failed.
```
これがでる
```
/* XXX Leave assert here, but handle __doc__ and the like better */
assert(scope || PyUnicode_READ_CHAR(name, 0) == '_');
```
ここらしい
なんもわからん
http://www.dzeta.jp/~junjis/code_reading/index.php?Python%2F%E3%83%90%E3%82%A4%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89%E7%94%9F%E6%88%90%E3%82%92%E8%AA%AD%E3%82%80
https://pf-siedler.hatenablog.com/entry/2016/11/05/135007
https://pf-siedler.hatenablog.com/entry/2016/10/29/132210
なんもわからん
```
static int
compiler_case(struct compiler *c, stmt_ty s, stmt_ty switch_s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL)return 0;
assert(s->kind == Case_kind);;
//constantは明らかに間違っているがあとでなおす
int constant = (switch_s->v.Switcha.test == s->v.Case.test)?1:0;
if(constant==0){//not match
BEGIN_DO_NOT_EMIT_BYTECODE
VISIT_SEQ(c, stmt, s->v.Case.body);
END_DO_NOT_EMIT_BYTECODE
}else{//match
VISIT_SEQ(c, stmt, s->v.Case.body);
}
if(s->v.Case.orelse){
stmt_ty nexts=s->v.Case.orelse;
compiler_use_next_block(c, next);
SET_LOC(c,nexts);
compiler_case(c,nexts,switch_s);
}
compiler_use_next_block(c, end);
return 1;
}
static int
compiler_switch(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL) {
return 0;
}
assert(s->kind == Switcha_kind);
stmt_ty switch_s=s;
stmt_ty nexts=s->v.Switcha.cases;
compiler_use_next_block(c, next);
SET_LOC(c, nexts);
compiler_case(c,nexts,switch_s);
compiler_use_next_block(c, end);
return 1;
}
```
# day6
実現したいバイトコードはたぶんこう?
これだとcaseのたびにswitchの右が評価されるので直感に反していそう
```
LOAD_NAME
LOAD_CONST
COMPARE_OP
POP_JUMP_IF_FALSE
(処理)
LOAD_NAME
LOAD_CONST
COMPARE_OP
POP_JUMP_IF_FALSE
(処理)
LOAD_NAME
...
```
え〜いとりあえずif文そのままにした(LOAD_NAMEが何回もできるようになる設計が思い浮かばないが)
python.gram
```
switcha_stmt[stmt_ty]:
| 'switcha' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, b)),EXTRA)}
case_block[stmt_ty]:
| 'case' a=atom ':' b=block c=case_block {_Py_Case(a,b, CHECK(_PyPegen_singleton_seq(p, c)),EXTRA)}
| 'case' a=atom ':' b=block c=[default_block] {_Py_Case(a,b,c,EXTRA)}
default_block[asdl_stmt_seq*]: 'defaulta' ':' b=block {b}
```
compile.c
```
static int
compiler_case(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL)return 0;
assert(s->kind == Case_kind);
int constant =1;// (switch_s->v.Switcha.test == s->v.Case.test)?1:0;
if(constant==0){//not match
BEGIN_DO_NOT_EMIT_BYTECODE
VISIT_SEQ(c, stmt, s->v.Case.body);
END_DO_NOT_EMIT_BYTECODE
}else{//match
VISIT_SEQ(c, stmt, s->v.Case.body);
}
if(s->v.Case.orelse){
compiler_use_next_block(c, next);
VISIT_SEQ(c,stmt,s->v.Case.orelse);
}
compiler_use_next_block(c, end);
return 1;
}
static int
compiler_switch(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL) {
return 0;
}
assert(s->kind == Switcha_kind);
compiler_use_next_block(c, next);
VISIT_SEQ(c,stmt,s->v.Switcha.cases);
compiler_use_next_block(c, end);
return 1;
}
```

条件調べるのむずかしそう…😨
VISITでtestを訪れることでLOAD_NAMEには成功するので、
```
static int compiler_addcompare(struct compiler *c, cmpop_ty op)
{
int cmp;
switch (op) {
case Eq:
cmp = Py_EQ;
break;
case NotEq:
cmp = Py_NE;
static int
compiler_compare(struct compiler *c, expr_ty e)
{
Py_ssize_t i, n;
if (!check_compare(c, e)) {
return 0;
}
VISIT(c, expr, e->v.Compare.left);
assert(asdl_seq_LEN(e->v.Compare.ops) > 0);
n = asdl_seq_LEN(e->v.Compare.ops) - 1;
if (n == 0) {
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, 0));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, 0));
}
```
ココらへんを参考にCOMPAREをaddすればよさそう
とりあえず、LOAD_CONSTをcaseの個数しなければならないところいがいについては実装できた
```
static int
compiler_case(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL)return 0;
assert(s->kind == Case_kind);
VISIT(c,expr,s->v.Case.test);
cmpop_ty op=Eq;
ADDOP_COMPARE(c,op);
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, next);
int constant =1;// (switch_s->v.Switcha.test == s->v.Case.test)?1:0;
if(constant==0){//not match
BEGIN_DO_NOT_EMIT_BYTECODE
VISIT_SEQ(c, stmt, s->v.Case.body);
END_DO_NOT_EMIT_BYTECODE
}else{//match
VISIT_SEQ(c, stmt, s->v.Case.body);
}
compiler_use_next_block(c, next);
if(s->v.Case.orelse){
VISIT_SEQ(c,stmt,s->v.Case.orelse);
}
compiler_use_next_block(c, end);
return 1;
}
static int
compiler_switch(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL) {
return 0;
}
assert(s->kind == Switcha_kind);
VISIT(c,expr,s->v.Switcha.test);
VISIT(c,expr,s->v.Switcha.test);
VISIT(c,expr,s->v.Switcha.test);
compiler_use_next_block(c, next);
VISIT_SEQ(c,stmt,s->v.Switcha.cases);
compiler_use_next_block(c, end);
return 1;
}
```

天才では?しかしこれではcaseの数が3以外のときにうまく行かないはずである。
あ、あと現時点ではdefaultが常に行われているようになっているので注意(あとで治す、まあbreakを勝手に行うようにしちゃえば問題なさそう)
どうやってcaseの個数だけLOAD_NAMEを行えばいいのだろうか…
DUP_TOPを使ったら出来た!!

というわけでswitchが実装できました。
変更点一覧(自動生成されているファイルも含む)
https://doss-gitlab.eidos.ic.i.u-tokyo.ac.jp/pythonista/pythonista/-/commit/c4ef8a949897e3083e51dba25ee1f4512113ef54
compile.c
```
static int
compiler_case(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL)return 0;
assert(s->kind == Case_kind);
ADDOP(c, DUP_TOP);
VISIT(c,expr,s->v.Case.test);
cmpop_ty op=Eq;
ADDOP_COMPARE(c,op);
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, next);
VISIT_SEQ(c, stmt, s->v.Case.body);
ADDOP_JUMP(c, JUMP_ABSOLUTE, end);
compiler_use_next_block(c, next);
if(s->v.Case.orelse){
VISIT_SEQ(c,stmt,s->v.Case.orelse);
}
compiler_use_next_block(c, end);
return 1;
}
static int
compiler_switch(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL) {
return 0;
}
assert(s->kind == Switcha_kind);
VISIT(c,expr,s->v.Switcha.test);
compiler_use_next_block(c, next);
VISIT_SEQ(c,stmt,s->v.Switcha.cases);
ADDOP(c, POP_TOP);
compiler_use_next_block(c, end);
return 1;
}
```
バイナリコード
```
1 0 LOAD_CONST 0 ('two')
2 STORE_NAME 0 (test)
2 4 LOAD_NAME 0 (test)
3 6 DUP_TOP
8 LOAD_CONST 1 ('one')
10 COMPARE_OP 2 (==)
12 POP_JUMP_IF_FALSE 24
4 14 LOAD_NAME 1 (print)
16 LOAD_CONST 1 ('one')
18 CALL_FUNCTION 1
20 POP_TOP
22 JUMP_ABSOLUTE 68
5 >> 24 DUP_TOP
26 LOAD_CONST 0 ('two')
28 COMPARE_OP 2 (==)
30 POP_JUMP_IF_FALSE 42
6 32 LOAD_NAME 1 (print)
34 LOAD_CONST 0 ('two')
36 CALL_FUNCTION 1
38 POP_TOP
40 JUMP_ABSOLUTE 68
7 >> 42 DUP_TOP
44 LOAD_CONST 2 ('zero')
46 COMPARE_OP 2 (==)
48 POP_JUMP_IF_FALSE 60
8 50 LOAD_NAME 1 (print)
52 LOAD_CONST 2 ('zero')
54 CALL_FUNCTION 1
56 POP_TOP
58 JUMP_ABSOLUTE 68
10 >> 60 LOAD_NAME 1 (print)
62 LOAD_CONST 3 ('default')
64 CALL_FUNCTION 1
66 POP_TOP
>> 68 POP_TOP
70 LOAD_CONST 4 (None)
72 RETURN_VALUE
```
## よくわからないこと
symtable.cってなにしてるの?シンボルテーブルってものを作ってるらしい。それはなにをしている?
# day7
## 提出物メモ
コード
スライド
レポート…ブログ形式
出来たことを書く
## やりたいことを決める
https://bugs.python.org/
からやりたいことを見つける
なかなかできそうなものがない…
https://cpython-core-tutorial.readthedocs.io/en/latest/where_should_i_start.html
によるとcpythonにコミットするのは難しいらしい
PEPってやつもあるらしい
https://www.python.org/dev/peps/
結局簡単そうなものが見つからず、最初に出していた案から次にやることを見つけることになった。
# day7.5
インクリメントのバグを直しておきたい
compile.cを
```
static int
compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx)
{
int op, scope;
Py_ssize_t arg;
enum { OP_FAST, OP_GLOBAL, OP_DEREF, OP_NAME } optype;
PyObject *dict = c->u->u_names;
PyObject *mangled;
assert(!_PyUnicode_EqualToASCIIString(name, "None") &&
!_PyUnicode_EqualToASCIIString(name, "True") &&
!_PyUnicode_EqualToASCIIString(name, "False"));
if (forbidden_name(c, name, ctx))
return 0;
mangled = _Py_Mangle(c->u->u_private, name);
if (!mangled)
return 0;
op = 0;
optype = OP_NAME;
scope = PyST_GetScope(c->u->u_ste, mangled);
switch (scope) {
case FREE:
dict = c->u->u_freevars;
optype = OP_DEREF;
break;
case CELL:
dict = c->u->u_cellvars;
optype = OP_DEREF;
break;
case LOCAL:
if (c->u->u_ste->ste_type == FunctionBlock)
optype = OP_FAST;
break;
case GLOBAL_IMPLICIT:
if (c->u->u_ste->ste_type == FunctionBlock)
optype = OP_GLOBAL;
break;
case GLOBAL_EXPLICIT:
optype = OP_GLOBAL;
break;
default:
/* scope can be 0 */
break;
}
/* XXX Leave assert here, but handle __doc__ and the like better */
assert(scope || PyUnicode_READ_CHAR(name, 0) == '_');
switch (optype) {
case OP_DEREF:
switch (ctx) {
case Load:
op = (c->u->u_ste->ste_type == ClassBlock) ? LOAD_CLASSDEREF : LOAD_DEREF;
break;
case Store: op = STORE_DEREF; break;
case Del: op = DELETE_DEREF; break;
}
break;
case OP_FAST:
switch (ctx) {
case Load: op = LOAD_FAST; break;
case Store: op = STORE_FAST; break;
case Del: op = DELETE_FAST; break;
}
ADDOP_N(c, op, mangled, varnames);
return 1;
case OP_GLOBAL:
switch (ctx) {
case Load: op = LOAD_GLOBAL; break;
case Store: op = STORE_GLOBAL; break;
case Del: op = DELETE_GLOBAL; break;
}
break;
case OP_NAME:
switch (ctx) {
case Load: op = LOAD_NAME; break;
case Store: op = STORE_NAME; break;
case Del: op = DELETE_NAME; break;
}
break;
}
assert(op);
arg = compiler_add_o(dict, mangled);
Py_DECREF(mangled);
if (arg < 0)
return 0;
return compiler_addop_i(c, op, arg);
}
```
をもとにして直しておけばいいはず
# day8
以下のように直したところ、インクリメントのバグがなおった。
ceval.c
```
case TARGET(UNARY_INCREMENT): {
PyObject *right = TOP();
PyObject *inv,*sum;
//-(~x)=x+1
inv=PyNumber_Invert(right);
if (inv == NULL)
goto error;
sum = PyNumber_Negative(inv);
Py_DECREF(inv);
if (sum == NULL)
goto error;
//DISPATCH();
/*
PyObject *names = f->f_code->co_names;
PyObject *name = GETITEM(names, ++oparg);
// PyObject *name = right;
PyObject *v = sum;
PyObject *ns = f->f_locals;
int err;
if (ns == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals found when storing %R", name);
Py_DECREF(v);
goto error;
}
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);
Py_DECREF(v);
if (err != 0)
goto error;
*/
Py_DECREF(right);
SET_TOP(sum);
DISPATCH();
}
```
compile.c(unaryop_kindのところ)
```
compiler_visit_expr1(struct compiler *c, expr_ty e)
{
switch (e->kind) {
case NamedExpr_kind:
VISIT(c, expr, e->v.NamedExpr.value);
ADDOP(c, DUP_TOP);
VISIT(c, expr, e->v.NamedExpr.target);
break;
case BoolOp_kind:
return compiler_boolop(c, e);
case BinOp_kind:
VISIT(c, expr, e->v.BinOp.left);
VISIT(c, expr, e->v.BinOp.right);
ADDOP(c, binop(e->v.BinOp.op));
break;
case UnaryOp_kind:
if(e->v.UnaryOp.op==UInc){
VISIT(c, expr, e->v.UnaryOp.operand);
ADDOP(c, unaryop(e->v.UnaryOp.op));
ADDOP(c, DUP_TOP);
assert( e->v.UnaryOp.operand->kind==Name_kind);
compiler_nameop(c,e->v.UnaryOp.operand->v.Name.id,Store);
/*
identifier name=e->v.UnaryOp.operand->v.Name.id;
PyObject *dict = c->u->u_names;
int op=STORE_NAME;
PyObject *mangled=_Py_Mangle(c->u->u_private, name);
Py_ssize_t arg = compiler_add_o(dict, mangled);
Py_DECREF(mangled);
if (arg < 0)
return 0;
return compiler_addop_i(c, op, arg);
*/
}else{
VISIT(c, expr, e->v.UnaryOp.operand);
ADDOP(c, unaryop(e->v.UnaryOp.op));
}
break;
case Lambda_kind:
```
全変更点一覧
https://doss-gitlab.eidos.ic.i.u-tokyo.ac.jp/pythonista/pythonista/-/commit/06c5b41f45dea105e67a7c245777d29b11979b03
# day9
残り時間でできることがなさそうなのでスライドを作り始めた。
## 全変更点まとめ
デモンストレーションのために全変更をまとめたブランチを作らなきゃ
### if then else
Grammer/python.gram L332
```
#ifdef DOSS_IFTHENELSE
| 'if' a=disjunction 'then' b=disjunction 'else' c=expression { _Py_IfExp(a, b, c, EXTRA) }
#endif
```
make regen-pegen
### switch
Grammer/python.gram L82
```
#ifdef DOSS_SWITCH
| &'switcha' switcha_stmt
#endif
```
L168
```
#ifdef DOSS_SWITCH
switcha_stmt[stmt_ty]:
| 'switcha' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, b)),EXTRA)}
case_block[stmt_ty]:
| 'case' a=atom ':' b=block c=case_block {_Py_Case(a,b, CHECK(_PyPegen_singleton_seq(p, c)),EXTRA)}
| 'case' a=atom ':' b=block c=[default_block] {_Py_Case(a,b,c,EXTRA)}
default_block[asdl_stmt_seq*]: 'defaulta' ':' b=block {b}
#endif
```
Parser/Python. L38
```
--#ifdef DOSS_SWITCH
| Switcha(expr test,stmt* cases)
| Case(expr test, stmt* body,stmt* orelse)
--#endif
```
Python/ast.c L404(要検証)
```
#ifdef DOSS_SWITCH
case Switcha_kind:
return 1;
case Case_kind:
return 1;
#endif
```
Python/compile.c L2746
```
#ifdef DOSS_SWITCH
static int
compiler_case(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL)return 0;
assert(s->kind == Case_kind);
ADDOP(c, DUP_TOP);
VISIT(c,expr,s->v.Case.test);
cmpop_ty op=Eq;
ADDOP_COMPARE(c,op);
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, next);
VISIT_SEQ(c, stmt, s->v.Case.body);
ADDOP_JUMP(c, JUMP_ABSOLUTE, end);
compiler_use_next_block(c, next);
if(s->v.Case.orelse){
VISIT_SEQ(c,stmt,s->v.Case.orelse);
}
compiler_use_next_block(c, end);
return 1;
}
static int
compiler_switch(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
next = compiler_new_block(c);
end = compiler_new_block(c);
if (next == NULL || end == NULL) {
return 0;
}
assert(s->kind == Switcha_kind);
VISIT(c,expr,s->v.Switcha.test);
compiler_use_next_block(c, next);
VISIT_SEQ(c,stmt,s->v.Switcha.cases);
ADDOP(c, POP_TOP);
compiler_use_next_block(c, end);
return 1;
}
#endif
```
L3457
```
#ifdef DOSS_SWITCH
case Switcha_kind:
return compiler_switch(c, s);
case Case_kind:
return compiler_case(c, s);
#endif
```
Python/symtable.c L1294
```
#ifdef DOSS_SWITCH
case Switcha_kind:
VISIT(st, expr, s->v.Switcha.test);
VISIT_SEQ(st, stmt, s->v.Switcha.cases);
break;
case Case_kind:
VISIT(st,expr,s->v.Case.test)
VISIT_SEQ(st, stmt, s->v.Case.body);
if (s->v.Case.orelse)
VISIT_SEQ(st, stmt, s->v.Case.orelse);
break;
#endif
```
### increment
Grammer/python.gram L458
```
#ifdef DOSS_INCREMENT
| '++' a=NAME { _Py_UnaryOp(UInc, a, EXTRA)}
#endif
```
Grammer/Tokens L56
```
#ifdef DOSS_INCREMENT
INCREMENT '++'
#endif
```
make regen-token
make regen-pegen
Parser/Python.asdl L99
```
--#ifdef DOSS_INCREMENT
unaryop = Invert | Not | UAdd | USub | UInc
--#endif
```
make regen-ast
(Include/Python-ast.h, Python/Python-ast.c, Programs/#python.c#, Parser/token.c, Parser/parser.c, Lib/token.py, Include/token.h, Doc/library/token-list.incなどは自動生成される)
Lib/opcode.py L68
```
#ifdef DOSS_INCREMENT
def_op('UNARY_INCREMENT', 13)
#endif
```
regen-opcode
regen-opcode-targets
(Include/opcode.hは自動生成される)
Python/compile.c
L905
```
#ifdef DOSS_INCREMENT
case UNARY_INCREMENT:
#endif
```
L3470
```
#ifdef DOSS_INCREMENT
case UInc:
return UNARY_INCREMENT;
#endif
```
L5014
```
case UnaryOp_kind:
#ifdef DOSS_INCREMENT
if(e->v.UnaryOp.op==UInc){
VISIT(c, expr, e->v.UnaryOp.operand);
ADDOP(c, unaryop(e->v.UnaryOp.op));
ADDOP(c, DUP_TOP);
assert( e->v.UnaryOp.operand->kind==Name_kind);
compiler_nameop(c,e->v.UnaryOp.operand->v.Name.id,Store);
}else{
#endif
VISIT(c, expr, e->v.UnaryOp.operand);
ADDOP(c, unaryop(e->v.UnaryOp.op));
#ifdef DOSS_INCREMENT
}
#endif
break;
```
Python/ceval.c L1619
```
#ifdef DOSS_INCREMENT
case TARGET(UNARY_INCREMENT): {
PyObject *right = TOP();
PyObject *inv,*sum;
//-(~x)=x+1
inv=PyNumber_Invert(right);
if (inv == NULL)
goto error;
sum = PyNumber_Negative(inv);
Py_DECREF(inv);
if (sum == NULL)
goto error;
Py_DECREF(right);
SET_TOP(sum);
DISPATCH();
}
#endif
```
Python/ast_unparse.c L179
```
#ifdef DOSS_INCREMENT
case UInc: op = "++";pr = PR_FACTOR; break;
#endif
```
コンパイル
CFLAGS="-O0 -pg -DDOSS_INCREMENT=1 -DDOSS_IFTHENELSE=1 -DDOSS_SWITCH=1" ./configure --with-openssl=/usr/local/openssl-1.1.0 --with-pydebug --prefix=/home/denjo/doss/cpython_install
## 時間が余ったらおまけ
switchでisで比較できるようになるには
割とめんどくさい気がしてきた
```
LOAD_NAME
PUSH(is,eq)
DUP_TOP_TWO
POP_TOP(is,eq)
LOAD_CONST
COMPARE_OP
POP_JUMP_IF_FALSE
(処理)
JUMP_ABSOLUTE
POP_TOP
POP_TOP
```
すればよさそう。
```
--#ifdef DOSS_SWITCH
| Switcha(expr test,stmt* cases,int? compare_type)
| Case(expr test, stmt* body,stmt* orelse)
--#endif
```
```
#ifdef DOSS_SWITCH
switcha_stmt[stmt_ty]:
| 'switcha' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, b)),0,EXTRA)}
switchb_stmt[stmt_ty]:
| 'switchb' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, b)),1,EXTRA)}
case_block[stmt_ty]:
| 'case' a=atom ':' b=block c=case_block {_Py_Case(a,b, CHECK(_PyPegen_singleton_seq(p, c)),EXTRA)}
| 'case' a=atom ':' b=block c=[default_block] {_Py_Case(a,b,c,EXTRA)}
default_block[asdl_stmt_seq*]: 'defaulta' ':' b=block {b}
#endif
```
バイトコード命令の一覧
https://docs.python.org/ja/3/library/dis.html
…できた。
https://doss-gitlab.eidos.ic.i.u-tokyo.ac.jp/pythonista/pythonista/-/commit/5d97958d02eaa69ba71acbce5a5e95dbd6435d43