1. Perl
  2. 言語実装の研究

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自体をソースコードからコンパイルする方法

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コアにはI32I16U32U16という型がでてきます。これらは整数型です。

  • 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で読み取りの終了を指定する。

業務に役立つPerl

Perlテキスト処理のエッセンス

PerlでポータブルなLinuxファイル管理入門

ITエンジニアの求人情報など

 ITエンジニアの求人情報・Webサービス・ソフトウェア・スクールなどの情報。

システム開発のお問い合わせ