- Perl ›
- 言語実装の研究
Perlの言語実装の研究
Perl言語の実装に関する自分用のメモ書きです。Perlのソースコードを読んで得られた知見をここに記述していきます。Perlの実装の全体像を、僕自身が理解していないので、理解は部分的なものであることを、ご了承ください。XSを書くときの参考にもなるかと思います。
ソースコードの概観
ソースコードについての簡単な解説。Perlのリポジトリには、たくさんのディレクトリとソースコードがありますが、それぞれがどのような機能の実装なのかを簡単に解説します。
perlmain.c | Perlのエントリポイント。miniperlmain.cから生成される。メモリ確保、字句解析、構文解析、実行、メモリ開放が順に呼び出される |
perl.h | Perlのヘッダ |
perl.c | Perlの実装。メモリ確保、字句解析、構文解析、実行、メモリ開放を呼び出す関数が定義されている |
interpvar.h | Perlのインタープリタ変数が定義されている |
perlvars.h | Perlのグローバル変数が定義されている |
embedvar.h | インタープリタ変数、グローバル変数の抽象化 |
parser.h | 字句解析と構文解析のためのヘッダ。パーサーの定義。 |
toke.c | 字句解析の実装。字句解析で生成されるトークンの種類はperly.yを見ればわかる。主要関数はyylex |
perly.y | yaccによる構文解析の定義。 |
perly.c | 構文解析の実装。トークンから、構文木を生成する。主要関数はyyparse |
opnames.h | オペレーションコードの種類 |
opcode.h | オペレーションに関する情報 |
op.c | 構文木のノードを生成する関数、最適化する関数が実装されている |
run.c | 構文解析によって作成された構文木を実行する |
scope.c | スコープに関する機能の実装。 |
cop.h | 次の実行文を実行するためのマクロが定義。ループの制御やnext,last。例外の実行など |
regen | ソースコードを自動生成するツールが格納されている。 |
sv.c | スカラ変数に関する実装 |
av.c | 配列に関する実装 |
hv.c | ハッシュに関する実装 |
pad.c | スコープ内で、レキシカル変数を保持するための機能の実装 |
pp.h pp_hot.h pp_sort.c pp_sys.c pp_pack.c pp_ctl.c | Perlの関数、演算子の実装 |
handy.h | 便利なマクロが定義されています |
util.c | 便利な関数が実装されています |
vutil.c | バージョンに関する解析関数 |
perlio.c | PerlのIOの実装 |
regcomp.c | 正規表現のコンパイルの実装 |
regexec.c | 正規表現の実行の実装 |
time64.c | 時刻の実装 |
utf8.c | UTF-8の実装 |
構文木のOPノードのタイプ
Perlは、コンパイルされる言語です。Perlのソースコードは、は、字句解析が終わった後に、構文解析され、構文木に、変換されます。
この構文木は「OP型」として表現されます。つまり、OP型のデータ構造が、木構造になってつながるわけです。これを便宜的に、OPノードと呼ぶことにします。
OPノードのタイプがわかれば、PerlがどのようなOPノードを持つかがわかります。その定義は「opnames.h」にあります。
// opnames.h typedef enum opcode { OP_NULL = 0, OP_STUB = 1, OP_SCALAR = 2, OP_PUSHMARK = 3, OP_WANTARRAY = 4, OP_CONST = 5, OP_GVSV = 6, OP_GV = 7, OP_GELEM = 8, OP_PADSV = 9, OP_PADAV = 10, OP_PADHV = 11, OP_PADANY = 12, OP_PUSHRE = 13, OP_RV2GV = 14, OP_RV2SV = 15, OP_AV2ARYLEN = 16, OP_RV2CV = 17, OP_ANONCODE = 18, OP_PROTOTYPE = 19, OP_REFGEN = 20, OP_SREFGEN = 21, OP_REF = 22, OP_BLESS = 23, OP_BACKTICK = 24, OP_GLOB = 25, OP_READLINE = 26, OP_RCATLINE = 27, OP_REGCMAYBE = 28, OP_REGCRESET = 29, OP_REGCOMP = 30, OP_MATCH = 31, OP_QR = 32, OP_SUBST = 33, OP_SUBSTCONT = 34, OP_TRANS = 35, OP_TRANSR = 36, OP_SASSIGN = 37, OP_AASSIGN = 38, OP_CHOP = 39, OP_SCHOP = 40, OP_CHOMP = 41, OP_SCHOMP = 42, OP_DEFINED = 43, OP_UNDEF = 44, OP_STUDY = 45, OP_POS = 46, OP_PREINC = 47, OP_I_PREINC = 48, OP_PREDEC = 49, OP_I_PREDEC = 50, OP_POSTINC = 51, OP_I_POSTINC = 52, OP_POSTDEC = 53, OP_I_POSTDEC = 54, OP_POW = 55, OP_MULTIPLY = 56, OP_I_MULTIPLY = 57, OP_DIVIDE = 58, OP_I_DIVIDE = 59, OP_MODULO = 60, OP_I_MODULO = 61, OP_REPEAT = 62, OP_ADD = 63, OP_I_ADD = 64, OP_SUBTRACT = 65, OP_I_SUBTRACT = 66, OP_CONCAT = 67, OP_STRINGIFY = 68, OP_LEFT_SHIFT = 69, OP_RIGHT_SHIFT = 70, OP_LT = 71, OP_I_LT = 72, OP_GT = 73, OP_I_GT = 74, OP_LE = 75, OP_I_LE = 76, OP_GE = 77, OP_I_GE = 78, OP_EQ = 79, OP_I_EQ = 80, OP_NE = 81, OP_I_NE = 82, OP_NCMP = 83, OP_I_NCMP = 84, OP_SLT = 85, OP_SGT = 86, OP_SLE = 87, OP_SGE = 88, OP_SEQ = 89, OP_SNE = 90, OP_SCMP = 91, OP_BIT_AND = 92, OP_BIT_XOR = 93, OP_BIT_OR = 94, OP_NBIT_AND = 95, OP_NBIT_XOR = 96, OP_NBIT_OR = 97, OP_SBIT_AND = 98, OP_SBIT_XOR = 99, OP_SBIT_OR = 100, OP_NEGATE = 101, OP_I_NEGATE = 102, OP_NOT = 103, OP_COMPLEMENT = 104, OP_NCOMPLEMENT = 105, OP_SCOMPLEMENT = 106, OP_SMARTMATCH = 107, OP_ATAN2 = 108, OP_SIN = 109, OP_COS = 110, OP_RAND = 111, OP_SRAND = 112, OP_EXP = 113, OP_LOG = 114, OP_SQRT = 115, OP_INT = 116, OP_HEX = 117, OP_OCT = 118, OP_ABS = 119, OP_LENGTH = 120, OP_SUBSTR = 121, OP_VEC = 122, OP_INDEX = 123, OP_RINDEX = 124, OP_SPRINTF = 125, OP_FORMLINE = 126, OP_ORD = 127, OP_CHR = 128, OP_CRYPT = 129, OP_UCFIRST = 130, OP_LCFIRST = 131, OP_UC = 132, OP_LC = 133, OP_QUOTEMETA = 134, OP_RV2AV = 135, OP_AELEMFAST = 136, OP_AELEMFAST_LEX = 137, OP_AELEM = 138, OP_ASLICE = 139, OP_KVASLICE = 140, OP_AEACH = 141, OP_AKEYS = 142, OP_AVALUES = 143, OP_EACH = 144, OP_VALUES = 145, OP_KEYS = 146, OP_DELETE = 147, OP_EXISTS = 148, OP_RV2HV = 149, OP_HELEM = 150, OP_HSLICE = 151, OP_KVHSLICE = 152, OP_MULTIDEREF = 153, OP_UNPACK = 154, OP_PACK = 155, OP_SPLIT = 156, OP_JOIN = 157, OP_LIST = 158, OP_LSLICE = 159, OP_ANONLIST = 160, OP_ANONHASH = 161, OP_SPLICE = 162, OP_PUSH = 163, OP_POP = 164, OP_SHIFT = 165, OP_UNSHIFT = 166, OP_SORT = 167, OP_REVERSE = 168, OP_GREPSTART = 169, OP_GREPWHILE = 170, OP_MAPSTART = 171, OP_MAPWHILE = 172, OP_RANGE = 173, OP_FLIP = 174, OP_FLOP = 175, OP_AND = 176, OP_OR = 177, OP_XOR = 178, OP_DOR = 179, OP_COND_EXPR = 180, OP_ANDASSIGN = 181, OP_ORASSIGN = 182, OP_DORASSIGN = 183, OP_METHOD = 184, OP_ENTERSUB = 185, OP_LEAVESUB = 186, OP_LEAVESUBLV = 187, OP_CALLER = 188, OP_WARN = 189, OP_DIE = 190, OP_RESET = 191, OP_LINESEQ = 192, OP_NEXTSTATE = 193, OP_DBSTATE = 194, OP_UNSTACK = 195, OP_ENTER = 196, OP_LEAVE = 197, OP_SCOPE = 198, OP_ENTERITER = 199, OP_ITER = 200, OP_ENTERLOOP = 201, OP_LEAVELOOP = 202, OP_RETURN = 203, OP_LAST = 204, OP_NEXT = 205, OP_REDO = 206, OP_DUMP = 207, OP_GOTO = 208, OP_EXIT = 209, OP_METHOD_NAMED = 210, OP_METHOD_SUPER = 211, OP_METHOD_REDIR = 212, OP_METHOD_REDIR_SUPER = 213, OP_ENTERGIVEN = 214, OP_LEAVEGIVEN = 215, OP_ENTERWHEN = 216, OP_LEAVEWHEN = 217, OP_BREAK = 218, OP_CONTINUE = 219, OP_OPEN = 220, OP_CLOSE = 221, OP_PIPE_OP = 222, OP_FILENO = 223, OP_UMASK = 224, OP_BINMODE = 225, OP_TIE = 226, OP_UNTIE = 227, OP_TIED = 228, OP_DBMOPEN = 229, OP_DBMCLOSE = 230, OP_SSELECT = 231, OP_SELECT = 232, OP_GETC = 233, OP_READ = 234, OP_ENTERWRITE = 235, OP_LEAVEWRITE = 236, OP_PRTF = 237, OP_PRINT = 238, OP_SAY = 239, OP_SYSOPEN = 240, OP_SYSSEEK = 241, OP_SYSREAD = 242, OP_SYSWRITE = 243, OP_EOF = 244, OP_TELL = 245, OP_SEEK = 246, OP_TRUNCATE = 247, OP_FCNTL = 248, OP_IOCTL = 249, OP_FLOCK = 250, OP_SEND = 251, OP_RECV = 252, OP_SOCKET = 253, OP_SOCKPAIR = 254, OP_BIND = 255, OP_CONNECT = 256, OP_LISTEN = 257, OP_ACCEPT = 258, OP_SHUTDOWN = 259, OP_GSOCKOPT = 260, OP_SSOCKOPT = 261, OP_GETSOCKNAME = 262, OP_GETPEERNAME = 263, OP_LSTAT = 264, OP_STAT = 265, OP_FTRREAD = 266, OP_FTRWRITE = 267, OP_FTREXEC = 268, OP_FTEREAD = 269, OP_FTEWRITE = 270, OP_FTEEXEC = 271, OP_FTIS = 272, OP_FTSIZE = 273, OP_FTMTIME = 274, OP_FTATIME = 275, OP_FTCTIME = 276, OP_FTROWNED = 277, OP_FTEOWNED = 278, OP_FTZERO = 279, OP_FTSOCK = 280, OP_FTCHR = 281, OP_FTBLK = 282, OP_FTFILE = 283, OP_FTDIR = 284, OP_FTPIPE = 285, OP_FTSUID = 286, OP_FTSGID = 287, OP_FTSVTX = 288, OP_FTLINK = 289, OP_FTTTY = 290, OP_FTTEXT = 291, OP_FTBINARY = 292, OP_CHDIR = 293, OP_CHOWN = 294, OP_CHROOT = 295, OP_UNLINK = 296, OP_CHMOD = 297, OP_UTIME = 298, OP_RENAME = 299, OP_LINK = 300, OP_SYMLINK = 301, OP_READLINK = 302, OP_MKDIR = 303, OP_RMDIR = 304, OP_OPEN_DIR = 305, OP_READDIR = 306, OP_TELLDIR = 307, OP_SEEKDIR = 308, OP_REWINDDIR = 309, OP_CLOSEDIR = 310, OP_FORK = 311, OP_WAIT = 312, OP_WAITPID = 313, OP_SYSTEM = 314, OP_EXEC = 315, OP_KILL = 316, OP_GETPPID = 317, OP_GETPGRP = 318, OP_SETPGRP = 319, OP_GETPRIORITY = 320, OP_SETPRIORITY = 321, OP_TIME = 322, OP_TMS = 323, OP_LOCALTIME = 324, OP_GMTIME = 325, OP_ALARM = 326, OP_SLEEP = 327, OP_SHMGET = 328, OP_SHMCTL = 329, OP_SHMREAD = 330, OP_SHMWRITE = 331, OP_MSGGET = 332, OP_MSGCTL = 333, OP_MSGSND = 334, OP_MSGRCV = 335, OP_SEMOP = 336, OP_SEMGET = 337, OP_SEMCTL = 338, OP_REQUIRE = 339, OP_DOFILE = 340, OP_HINTSEVAL = 341, OP_ENTEREVAL = 342, OP_LEAVEEVAL = 343, OP_ENTERTRY = 344, OP_LEAVETRY = 345, OP_GHBYNAME = 346, OP_GHBYADDR = 347, OP_GHOSTENT = 348, OP_GNBYNAME = 349, OP_GNBYADDR = 350, OP_GNETENT = 351, OP_GPBYNAME = 352, OP_GPBYNUMBER = 353, OP_GPROTOENT = 354, OP_GSBYNAME = 355, OP_GSBYPORT = 356, OP_GSERVENT = 357, OP_SHOSTENT = 358, OP_SNETENT = 359, OP_SPROTOENT = 360, OP_SSERVENT = 361, OP_EHOSTENT = 362, OP_ENETENT = 363, OP_EPROTOENT = 364, OP_ESERVENT = 365, OP_GPWNAM = 366, OP_GPWUID = 367, OP_GPWENT = 368, OP_SPWENT = 369, OP_EPWENT = 370, OP_GGRNAM = 371, OP_GGRGID = 372, OP_GGRENT = 373, OP_SGRENT = 374, OP_EGRENT = 375, OP_GETLOGIN = 376, OP_SYSCALL = 377, OP_LOCK = 378, OP_ONCE = 379, OP_CUSTOM = 380, OP_COREARGS = 381, OP_RUNCV = 382, OP_FC = 383, OP_PADCV = 384, OP_INTROCV = 385, OP_CLONECV = 386, OP_PADRANGE = 387, OP_REFASSIGN = 388, OP_LVREF = 389, OP_LVREFSLICE = 390, OP_LVAVREF = 391, OP_ANONCONST = 392, OP_max } opcode;
Perlの構文解析器の構造
Perlの構文解析器はbisonを使って生成されます。bisonとは構文解析を行うツールyaccに機能を追加したツールだと考えて下さ。
Perlの構文規則は「perly.y」に記述されており、このファイルがbinsonコマンドによって、以下のファイルに変換されます。
perly.h
perly.tab
perly.act
これらのファイルはC言語のファイルです。つまり「perly.y」に記述された構文規則が、C言語のコードに変換されます。
「perly.c」というのが、Perlの構文解析のソースコードですが、この内部で「perly.act」がインクルードされています。
// perly.c int Perl_yyparse (pTHX_ int gramtype) { // ... // トーカナイズ parser->yychar = yylex(); // ... /* 構文解析 #include "perly.act" // ... }
構文解析の前には、トークンを生成しなければなりませんが、これは「toke.c」に記述されています。yylexという関数がそれです。
// toke.c int Perl_yylex(pTHX) { // ... }
パーサーのスタックフレーム情報
ソースコードをトーカナイズするときに、スタックフレームというのが、利用されます。
// parser.h typedef struct { YYSTYPE val; /* semantic value */ short state; I32 savestack_ix; /* size of savestack at this state */ CV *compcv; /* value of PL_compcv when this value was created */ } yy_stack_frame; // parly.h typedef union YYSTYPE { /* Line 2058 of yacc.c */ I32 ival; /* __DEFAULT__ (marker for regen_perly.pl; must always be 1st union member) */ char *pval; OP *opval; GV *gvval; /* Line 2058 of yacc.c */ } YYSTYPE;
aTHX_は、スレッドが有効にされてPerlがコンパイルされた場合に、展開される
aTHX_は、スレッドが有効にされてPerlがコンパイルされた場合に、現在のスレッドに展開されます。
// perl.h # define aTHX_ aTHX, # define aTHX my_perl
スレッドが有効にされていない場合は次のように展開され、何もしません。
// perl.h # define aTHX # define aTHX_
省略文字の予想
変数に使われている省略文字でだいたい予想できるので、よく使われているものをピックアップ。
- fn - finishの略
- ix - indexの略
Safefreeは、単なるメモリ開放だ
Safefreeは、変数のメモリを開放するときに使われるが、単なるメモリ開放だ。freeと考えるのがよい。さまざまな条件に対応できるように、処理が隠蔽されている。
// 使用例 Safefree(PL_exitlist);
実装。
// handy.h #define Safefree(d) safefree(MEM_LOG_FREE((Malloc_t)(d))) // per.h # define safefree safesysfree // embed.h #define safesysfree Perl_safesysfree // util.h Free_t Perl_safesysfree(Malloc_t where) { // ... if (where) { Malloc_t where_intrn = where; PerlMem_free(where_intrn); } } //iperlsys.h #define PerlMem_free(buf) free((buf))
SvREFCNT_decはSV型の変数のメモリを開放する場合に行われる。
perl_destructの中では、Perlで利用されている変数のメモリの開放処理が行われるが、これは以下のように行われる。
SvREFCNT_dec(PL_ofsgv); PL_ofsgv = NULL;
SV型の変数が作成される場合には、SvREFCNT_incによって、リファレンスカウントが1増やされているので、メモリの開放処理を行う場合には、SvREFCNT_decによって、リファレンスカウントを1減らしてやる。
このようにすることで、SVのメモリ開放処理機構で、最終的には、メモリが開放される。
参照元の変数には、NULLが代入される。
perl_parseの中身の処理の骨格
Perlスクリプトを解析するときに、perl_parseという関数が呼び出されますが、この関数の内部の骨格を取り出しておきます。
// perl.c int perl_parse(pTHXx_ XSINIT_t xsinit, int argc, char **argv, char **env) { // ... parse_body(env,xsinit); // ... }
parl_parseからは、parse_bodyが呼び出されます。コマンドラインオプションの解析、スクリプトをファイルオープン、レキサーの初期化、レキサーによるPerlスクリプトの解析が実効されます。
// embed.h #define parse_body(a,b) S_parse_body(aTHX_ a,b) // perl.c STATIC void * S_parse_body(pTHX_ char **env, XSINIT_t xsinit) { // コマンドラインオプションの解析などがある // スクリプトをファイルオープン rsfp = open_script(scriptname, dosearch, &suidscript); // レキサー(トーカナイザー)の初期化 lex_start(linestr_sv, rsfp, lex_start_flags); // レキサーによるPerlスクリプトの解析の実行 if (yyparse(GRAMPROG) || PL_parser->error_count) { } }
これが終わった段階で、Perlスクリプトは、トークンと呼ばれる解析の最小単位まで、分解されます。
Perlインタープリタの骨格だけを取り出すと
Perlインタープリタの骨格だけを取り出してみました。以下のような処理の流れになっています。この骨格の部分から、細部にたどっていくと、Perl自体のソースコードを読んでいくのによいと思います。
// miniperlmain.c int main(int argc, char **argv, char **env) { // メモリ割り当て my_perl = perl_alloc(); // 必要な情報を構築、初期化など perl_construct(my_perl); // Perlスクリプトを解析 perl_parse(my_perl, xs_init, argc, argv, (char **)NULL); // 解析されたPerlスクリプトを実効 perl_run(my_perl); // Perlの情報を破棄 perl_destruct(my_perl); // メモリの解放 perl_free(my_perl); }
骨格だけを見ると、非常にシンプルです。
EXTERN.hとINTERN.hの違い
Perlには、EXTERN.hとINTERN.hというものがあるが、用途としては、EXTERN.hというのは、Perl自体を、拡張モジュールから参照するときに使用します。Perl自体が持っているグローバル変数を、拡張モジュールから見えるようにするために、EXTERN.hを読み込む必要があります。
一方、INTERN.hというのは、Perl自体をコンパイルするときに、使用されます。グローバル変数の値の設定などが行われます。
SVp_IOKは、公開されていない有効な整数を持っていることを示します。
SVp_IOKは、公開されていない有効な整数を持っていることを示します。一方、SVf_IOKは、公開されている有効な整数を持っていることを示します。
公開というのは、どういう意味だろう。
SVt_PVIVというのは、SVt_PVとSVt_IVの両方が設定されていることを意味する。
SVt_PVは、SVが文字列であること、SVt_IVは、SVが整数であることを示します。SVt_PVIVは、SVが文字列であり、整数であることを示します。
new_XPVNV
XPVNVというのは、SV型のsv_anyに代入されるものです。これは、数値を表現します。
// sv.c #define new_XPVNV() new_body_allocated(SVt_PVNV) // sv.c #define new_body_allocated(sv_type) \ (void *)((char *)S_new_body(aTHX_ sv_type) \ - bodies_by_type[sv_type].offset) // sv.c STATIC void * S_new_body(pTHX_ const svtype sv_type) { void *xpv; new_body_inline(xpv, sv_type); return xpv; } // sv.c #define new_body_inline(xpv, sv_type) \ STMT_START { \ void ** const r3wt = &PL_body_roots[sv_type]; \ xpv = (PTR_TBL_ENT_t*) (*((void **)(r3wt)) \ ? *((void **)(r3wt)) : Perl_more_bodies(aTHX_ sv_type, \ bodies_by_type[sv_type].body_size,\ bodies_by_type[sv_type].arena_size)); \ *(r3wt) = *(void**)(xpv); \ } STMT_END // sv.c /* PL_body_roots[] array of pointers to list of free bodies of svtype arrays are indexed by the svtype needed */
SV型の定義
SV型の定義です。
// perl.h typedef struct STRUCT_SV SV; // perl.h /* SGI's <sys/sema.h> has struct sv */ #if defined(__sgi) # define STRUCT_SV perl_sv #else # define STRUCT_SV sv #endif // sv.h struct STRUCT_SV { /* struct sv { */ _SV_HEAD(void*); _SV_HEAD_UNION; }; // sv.h /* start with 2 sv-head building blocks */ #define _SV_HEAD(ptrtype) \ ptrtype sv_any; /* pointer to body */ \ U32 sv_refcnt; /* how many references to us */ \ U32 sv_flags /* what we are */ // sv.h #define _SV_HEAD_UNION \ union { \ char* svu_pv; /* pointer to malloced string */ \ IV svu_iv; \ UV svu_uv; \ _NV_BODYLESS_UNION \ SV* svu_rv; /* pointer to another SV */ \ struct regexp* svu_rx; \ SV** svu_array; \ HE** svu_hash; \ GP* svu_gp; \ PerlIO *svu_fp; \ } sv_u \ _SV_HEAD_DEBUG
sv_anyは、SVが保持する実際の値を格納する構造体のポインタ。sv_refcntは、リファレンスカウント、sv_flagは、SVに関する種類とフラグ、たとえば、種類は、整数、フラグは読み込み専用など、複数指定可能。sv_uというのは、どのように利用されるのかは、ちょっとわからない。
SV型の種類
SV型というのは、拡張することができ、さまざまなものになることができるが、これらのタイプは、svtypeとして、実装されています。
typedef enum { SVt_NULL, /* 0 */ /* BIND was here, before INVLIST replaced it. */ SVt_IV, /* 1 */ SVt_NV, /* 2 */ /* RV was here, before it was merged with IV. */ SVt_PV, /* 3 */ SVt_INVLIST, /* 4, implemented as a PV */ SVt_PVIV, /* 5 */ SVt_PVNV, /* 6 */ SVt_PVMG, /* 7 */ SVt_REGEXP, /* 8 */ /* PVBM was here, before BIND replaced it. */ SVt_PVGV, /* 9 */ SVt_PVLV, /* 10 */ SVt_PVAV, /* 11 */ SVt_PVHV, /* 12 */ SVt_PVCV, /* 13 */ SVt_PVFM, /* 14 */ SVt_PVIO, /* 15 */ SVt_LAST /* keep last in enum. used to size arrays */ } svtype;
Perlは複数のインタープリタを持つことができる
ソースコードを読む上で、理解しておくべきことは、Perlは複数のインタープリタを持つことが可能で、関数などからは、現在のインタープリタの情報にアクセスしているということを理解しておきましょう。
Perl - Perlインタープリタ1(現在のインタープリタ) - Perlインタープリタ2 - Perlインタープリタ3
デフォルトでは、ひとつのインタープリタだけが動いていると想像して、ソースコードを追うのがよいでしょう。
dVarは特に意識しないでよい
dVarという宣言がときどきでてくるが、特に意識しなくてよいと思われる。
#ifdef PERL_GLOBAL_STRUCT # define dVAR pVAR = (struct perl_vars*)PERL_GET_VARS() #else # define dVAR dNOOP #endif #define pVAR struct perl_vars* my_vars PERL_UNUSED_DECL
PERL_GLOBAL_STRUCTが定義されるのは、OSがsymbianの場合だけのようにソースコードからは見える。であるとすれば、ほとんどのOSの場合は、dVARがdNOOPとなって、dNOOPは何もしないという意味なので、dVarは何もしない。
// symbian/PerlApp.cpp: #define PERL_GLOBAL_STRUCT // symbian/PerlBase.h: # define PERL_GLOBAL_STRUCT
システムコールのラッパーはiperlsys.hに記述されている
Perlのソースコードでは、mallocなどを直接呼ぶことはない。システムコールへのラッパー関数あるいはマクロを使ってアクセスするが、このラッパーの定義は「iperlsys.h」に記述されている。
iperlsys.h
perl_allocは、メモリを割り当てて0で初期化するだけ
perl_allocは、メモリを割り当てて0で初期化するだけ。
楽に読むコツ
- ASSERTが入っているマクロは単にassertを呼んでいるだけなので、無視する
- PERL_UNUSED_ARGは、使用されていない変数に対する警告をなくすだけなので、無視する
- 使用されている多くのマクロはperl.hかproto.hかhandy.hにある
- ヘルパ的な関数はutil.cにあることが多い
- #ifdefの中は特殊なカスタマイズなことが多いので、とりあえず、飛ばす
- #ifndefの中は読む、#ifdef ~ #else ~ の#elseのあとは読む
- コードの中のスレッドの記述はいったん忘れよう
Perlのソースコードをコンパイルする方法
Perlのソースコードをコンパイルする方法は以下に記載。Perlのエントリポイントがあるのはperlmain.c
Perlのエントリポイントがあるのはperlmain.c。しかし、このファイルは、Perlのソースコードに含まれない。
以下のMakefileの中に、生成される記述がある。
# Cross/Makefile-cross-SH perlmain.c: miniperlmain.c config.sh $(FIRSTMAKEFILE) sh writemain $(DYNALOADER) $(static_ext) > perlmain.c
makeを実行すれば「perlmain.c」が生成されます。
また「perlmain.c」というのは「miniperlmain.c」の記述とほぼ同じで、次の行が追加されただけのものになっています。
/* Register any extra external extensions */ EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);
つまり「miniperlmain.c」に、ダイナミックロードの機能を付け加えたものが、「perlmain.c」ということです。ですから、「miniperlmain.c」をみれば、ほぼすべてのことがわかります。
OP型はop型でありop.hで定義されている
オペレーターの情報を保存するOP型は、op.hで定義されている。
// perl.h typedef struct op OP; // op.h struct op { BASEOP }; // op.h #ifdef BASEOP_DEFINITION #define BASEOP BASEOP_DEFINITION #else #define BASEOP \ OP* op_next; \ OP* _OP_SIBPARENT_FIELDNAME;\ OP* (*op_ppaddr)(pTHX); \ PADOFFSET op_targ; \ PERL_BITFIELD16 op_type:9; \ PERL_BITFIELD16 op_opt:1; \ PERL_BITFIELD16 op_slabbed:1; \ PERL_BITFIELD16 op_savefree:1; \ PERL_BITFIELD16 op_static:1; \ PERL_BITFIELD16 op_folded:1; \ PERL_BITFIELD16 op_moresib:1; \ PERL_BITFIELD16 op_spare:1; \ U8 op_flags; \ U8 op_private; #endif /* * The fields of BASEOP are: * op_next Pointer to next ppcode to execute after this one. * (Top level pre-grafted op points to first op, * but this is replaced when op is grafted in, when * this op will point to the real next op, and the new * parent takes over role of remembering starting op.) * op_ppaddr Pointer to current ppcode's function. * op_type The type of the operation. * op_opt Whether or not the op has been optimised by the * peephole optimiser. * op_slabbed allocated via opslab * op_static tell op_free() to skip PerlMemShared_free(), when * !op_slabbed. * op_savefree on savestack via SAVEFREEOP * op_folded Result/remainder of a constant fold operation. * op_moresib this op is is not the last sibling * op_spare One spare bit * op_flags Flags common to all operations. See OPf_* below. * op_private Flags peculiar to a particular operation (BUT, * by default, set to the number of children until * the operation is privatized by a check routine, * which may or may not check number of children). */
Perlのレキサー(トーカナイザー)のメインルーチンはyylex
Perlのレキサー(トーカナイザー)のメインルーチンはyylex。
// toke.c int Perl_yylex(pTHX) { dVAR; char *s = PL_bufptr; char *d; STRLEN len; bool bof = FALSE; const bool saw_infix_sigil = cBOOL(PL_parser->saw_infix_sigil); U8 formbrack = 0; U32 fake_eof = 0; // ... }
Perlスクリプトは、このルーチンによって、トークンまで、分解される。
YYSTYPEはperly.hで定義されている
YYSTYPEはperly.hで定義されている。
PL_parserはyy_parserと同じ
embedvar.hの記述とintrpvar.hの記述で、yy_parserにはPL_parserという名前が与えられる。
// embedvar.h #define PL_parser (vTHX->Iparser) // intrpvar.h PERLVAR(I, parser, yy_parser *) /* current parser state */ // parser.h typedef struct yy_parser { /* parser state */ struct yy_parser *old_parser; /* previous value of PL_parser */ YYSTYPE yylval; /* value of lookahead symbol, set by yylex() */ int yychar; /* The lookahead symbol. */ /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; int stack_size; int yylen; /* length of active reduction */ yy_stack_frame *stack; /* base of stack */ yy_stack_frame *ps; /* current stack frame */ /* lexer state */ I32 lex_brackets; /* square and curly bracket count */ I32 lex_casemods; /* casemod count */ char *lex_brackstack;/* what kind of brackets to pop */ char *lex_casestack; /* what kind of case mods in effect */ U8 lex_defer; /* state after determined token */ U8 lex_dojoin; /* doing an array interpolation 1 = @{...} 2 = ->@ */ U8 lex_expect; /* UNUSED */ U8 expect; /* how to interpret ambiguous tokens */ I32 lex_formbrack; /* bracket count at outer format level */ OP *lex_inpat; /* in pattern $) and $| are special */ OP *lex_op; /* extra info to pass back on op */ SV *lex_repl; /* runtime replacement from s/// */ U16 lex_inwhat; /* what kind of quoting are we in */ OPCODE last_lop_op; /* last named list or unary operator */ I32 lex_starts; /* how many interps done on level */ SV *lex_stuff; /* runtime pattern from m// or s/// */ I32 multi_start; /* 1st line of multi-line string */ I32 multi_end; /* last line of multi-line string */ char multi_open; /* delimiter of said string */ char multi_close; /* delimiter of said string */ bool preambled; bool lex_re_reparsing; /* we're doing G_RE_REPARSING */ I32 lex_allbrackets;/* (), [], {}, ?: bracket count */ SUBLEXINFO sublex_info; LEXSHARED *lex_shared; SV *linestr; /* current chunk of src text */ char *bufptr; /* carries the cursor (current parsing position) from one invocation of yylex to the next */ char *oldbufptr; /* in yylex, beginning of current token */ char *oldoldbufptr; /* in yylex, beginning of previous token */ char *bufend; char *linestart; /* beginning of most recently read line */ char *last_uni; /* position of last named-unary op */ char *last_lop; /* position of last list operator */ /* copline is used to pass a specific line number to newSTATEOP. It is a one-time line number, as newSTATEOP invalidates it (sets it to NOLINE) after using it. The purpose of this is to report line num- bers in multiline constructs using the number of the first line. */ line_t copline; U16 in_my; /* we're compiling a "my"/"our" declaration */ U8 lex_state; /* next token is determined */ U8 error_count; /* how many compile errors so far, max 10 */ HV *in_my_stash; /* declared class of this "my" declaration */ PerlIO *rsfp; /* current source file pointer */ AV *rsfp_filters; /* holds chain of active source filters */ U8 form_lex_state; /* remember lex_state when parsing fmt */ YYSTYPE nextval[5]; /* value of next token, if any */ I32 nexttype[5]; /* type of next token */ U32 nexttoke; COP *saved_curcop; /* the previous PL_curcop */ char tokenbuf[256]; line_t herelines; /* number of lines in here-doc */ line_t preambling; /* line # when processing $ENV{PERL5DB} */ U8 lex_fakeeof; /* precedence at which to fake EOF */ U8 lex_flags; PERL_BITFIELD16 in_pod:1; /* lexer is within a =pod section */ PERL_BITFIELD16 filtered:1; /* source filters in evalbytes */ PERL_BITFIELD16 saw_infix_sigil:1; /* saw & or * or % operator */ PERL_BITFIELD16 parsed_sub:1; /* last thing parsed was a sub */ } yy_parser;
PADLIST
PADLISTはレキシカル変数の一覧です。
// perl.h struct padlist PADLIST; // pad.h struct padlist { SSize_t xpadl_max; /* max index for which array has space */ union { PAD ** xpadlarr_alloc; /* Pointer to beginning of array of AVs. index 0 is a padnamelist * */ struct { PADNAMELIST * padnl; PAD * pad_1; /* this slice of PAD * array always alloced */ PAD * pad_2; /* maybe unalloced */ } * xpadlarr_dbg; /* for use with a C debugger only */ } xpadl_arr; U32 xpadl_id; /* Semi-unique ID, shared between clones */ U32 xpadl_outid; /* ID of outer pad */ };
PADNAMELIST
// perl.h typedef struct padnamelist PADNAMELIST; // pad.h struct padnamelist { SSize_t xpadnl_fill; /* max index in use */ PADNAME ** xpadnl_alloc; /* pointer to beginning of array */ SSize_t xpadnl_max; /* max index for which array has space */ PADOFFSET xpadnl_max_named; /* highest index with len > 0 */ U32 xpadnl_refcnt; };
PADNAME
// perl.h typedef struct padname PADNAME; // #define _PADNAME_BASE \ char * xpadn_pv; \ HV * xpadn_ourstash; \ union { \ HV * xpadn_typestash; \ CV * xpadn_protocv; \ } xpadn_type_u; \ U32 xpadn_low; \ U32 xpadn_high; \ U32 xpadn_refcnt; \ int xpadn_gen; \ U8 xpadn_len; \ U8 xpadn_flags struct padname { _PADNAME_BASE; };
ANY
ANYは、なんでも代入することができるデータ構造です。
// perl.h typedef union any ANY; #ifdef UNION_ANY_DEFINITION UNION_ANY_DEFINITION; #else union any { void* any_ptr; I32 any_i32; U32 any_u32; IV any_iv; UV any_uv; long any_long; bool any_bool; void (*any_dptr) (void*); void (*any_dxptr) (pTHX_ void*); }; #endif
データ構造が配列を意図しているか、ポインタを意図しているか意識する
// 整数の配列が意図されているのか、単一の整数へのポインタを意図しているのか PERLVAR(I, markstack_max, I32 *)
STMT_STARTとSTMT_ENDはマクロの中の文を安全に実行する仕組み
STMT_STARTとSTMT_ENDはマクロの中の文を安全に実行する仕組み。「do {」と「} while (0)」に該当すると考えればよい。
//マクロの本体を do-while ループの中に包み込むことで、マクロを安全に実行するための仕組み。 #define SWAP(x, y) \ do { \ tmp = x; \ x = y; \ y = tmp; } \ while (0)
PoisonNewは、ひとつのパターンで埋める
PoisonNewは、ひとつのパターンで埋める。memsetだと思えばよい。
// handy.h #define PoisonNew(d,n,t) PoisonWith(d,n,t,0xAB) #define PoisonWith(d,n,t,b) (MEM_WRAP_CHECK_(n,t) (void)memset((char*)(d), (U8)(b), (n) * sizeof(t)))
Renewはreallocのことだ
Renewはreallocのことだ。確保するメモリがはみ出さないかのチェックも合わせておこなってくれるよう。
// handy.h #define Renew(v,n,t) \ (v = (MEM_WRAP_CHECK_(n,t) (t*)MEM_LOG_REALLOC(n,t,v,saferealloc((Malloc_t)(v),(MEM_SIZE)((n)*sizeof(t)))))) #define Renewc(v,n,t,c) \ (v = (MEM_WRAP_CHECK_(n,t) (c*)MEM_LOG_REALLOC(n,t,v,saferealloc((Malloc_t)(v),(MEM_SIZE)((n)*sizeof(t))))))
nはポインタ、nはサイズ、tは型。確保されるサイズはn×t。
STRESS_REALLOCとは何かわからない
STRESS_REALLOCとは何かわからない。どこで定義される可能性があるのか?
PERL_SIはスタック情報
PERL_SIはスタック情報。
// cop.h struct stackinfo { AV * si_stack; /* stack for current runlevel */ PERL_CONTEXT * si_cxstack; /* context stack for runlevel */ struct stackinfo * si_prev; struct stackinfo * si_next; I32 si_cxix; /* current context index */ I32 si_cxmax; /* maximum allocated index */ I32 si_type; /* type of runlevel */ I32 si_markoff; /* offset where markstack begins for us. * currently used only with DEBUGGING, * but not #ifdef-ed for bincompat */ }; typedef struct stackinfo PERL_SI;
PL_で始まる変数は、グローバル変数のこともある
PL_で始まる変数は、グローバル変数のこともあります。PL_YesやPL_Noは、perl.hで定義されています。
// perl.h
EXTCONST char PL_Yes[]
INIT("1");EXTCONST char PL_No[]
INIT("");
PL_で始まる変数名は、インタープリタ変数
PL_で始まる変数名は、インタープリタ変数です。現在実行されているPerlインタープリタの情報を持っている変数です。embedvar.hで定義されています。
// embedvar.h #if defined(MULTIPLICITY) /* cases 2 and 3 above */ # if defined(PERL_IMPLICIT_CONTEXT) # define vTHX aTHX # else # define vTHX PERL_GET_INTERP # endif #define PL_AboveLatin1 (vTHX->IAboveLatin1) #define PL_Argv (vTHX->IArgv) #define PL_Cmd (vTHX->ICmd) #define PL_DBcontrol (vTHX->IDBcontrol) #define PL_DBcv (vTHX->IDBcv)
さらに探るには「aTHX」がどのようなものかを見ます。
// perl.h # define aTHX my_perl // perl.h PerlInterpreter *my_perl;
これは「PerlInterpreter」という型のようです。この構造体の定義は「intrpvar.h」というファイルでなされています。
// intrpvar.h PERLVAR(I, Latin1, SV *) PERLVAR(I, UpperLatin1, SV *) /* Code points 128 - 255 */ PERLVAR(I, AboveLatin1, SV *) PERLVAR(I, InBitmap, SV *)
PERLVARというマクロで「IAboveLatin1」のような結合された変数名が生成されます。
UNLIKELYはperl.hで記述されているマクロ
これは、条件が偽である場合に、最適化されるコードになっていて、意味的には、単なるブールである。
// perl.h #define UNLIKELY(cond) EXPECT(cBOOL(cond),FALSE) // perl.h #ifdef HAS_BUILTIN_EXPECT # define EXPECT(expr,val) __builtin_expect(expr,val) #else # define EXPECT(expr,val) (expr) #endif // handy.h #define cBOOL(cbool) ((cbool) ? (bool)1 : (bool)0) // handy.h #ifndef HAS_BOOL # ifdef bool # undef bool # endif # define bool char # define HAS_BOOL 1 #endif
「__builtin_expect(A,B)」と書いた場合 Aが定数 Bである事を期待する、というヒント情報になる。
以下のふたつは、条件分岐としてはまったく同じものであるが、UNLIKLYの場合は、偽であることが期待されて、最適化される。
if (条件) { ... } if (UNLIKLY(条件)) { ... }
またLIKELYというのもあって、こちらは、真であることが期待されて最適化される。
I32やI16というのは整数型のこと
PerlコアにはI32やI16やU32やU16という型がでてきます。これらは整数型です。
- I32 - 最低32ビットを持つ整数型
- I16 - 最低16ビットを持つ整数型
- U32 - 最低32ビットを持つ符号なし整数型
- U16 - 最低16ビットを持つ符号なし整数型
なぜintやlongを直接使わないかという理由は考え中ですが、おそらくPerlがたくさんのOSで実行されるので、整数型を抽象化するためでしょう。(perlgutsには「これらは普通は正確に 32 ビットと 16 ビットですが、Cray(というOS)では両方とも 64 ビットです」と書いてあります。)
SVというのはスカラ型のこと
SVというのはスカラ型のことです。
SV *sv;
AVというのは配列型のこと
AVというのは配列型のことです。
AV *array;
GVというのはグロブ型のこと
GVというのはグロブ型のことです。
GV *gv;
HVというのはハッシュ型のこと
HVというのはハッシュ型のことです。
HV *hash;
CVというのは関数型のこと
CVというのは関数型のことです。
CV *cv;
pvというのは文字列のこと
最近はPerlのコアを呼んでいる。省略が多いので読みにくいけれど、googleで検索してもあんまり情報が出てこないので、メモします。
pvというのは文字列のこと。pvがと関数名に含まれていたら文字列に関連する関数なんだなぁと考える。
pvnというのは、文字列と文字列の長さを指定する関数に含まれる。nはnumberのnかなぁ。
パフォーマンス的にはpvnのほうが自分で長さを指定する分パフォーマンスがたぶん早いのでしょう。
mgというのはマジックのこと
mgというのはマジックのことです。マジックというのは、まぁマジックです。付加的な処理かなぁ。
Newxはメモリを確保する
Newxはメモリを確保します。内部では結構複雑なことを行っていますが、まぁ感覚としてはmallocと思えばいいんじゃないでしょうか。新品のメモリ領域という感じですね。
constは定数を意味する
PerlのコアはC言語で書かれている。だからC言語が読めないとPerlのコアは読めない。なのでC言語の情報もメモしておきます。
constは定数を意味します。つまり書き換えられない読み込み専用の変数ということです。
constが3回も出てきた
Perlのコアを読んでいたら、次のようなのに遭遇。
const char *const *const search_ext
constが三回もでてきてどういう意味かなと調べたら、constの対象が違うのだった。最初のconstはポインタのポインタのさす先の変数に対するconst、二つ目のconstはポインタのポインタに対するconst、三つ目のはポインタ(search_ext)に対するconstなのでした。
va_listは可変個引数を受け取る
C言語では可変個引数を受け取る方法が用意されている。...を引数の部分に記述すると可変個引数を受け取ることができる。
型 foo(引数1, ...);
これを関数の中で利用するには次のようにする。
va_list args; va_start(args, 引数1); while (条件) 型 arg = va_arg(args, 型); } va_end(args);
va_listは可変長引数オブジェクト。va_startで可変長引数が始まる直前の引数名を指定する。va_argで可変長引数を順番に受け取ることができる。va_endで読み取りの終了を指定する。