はじめに
自作のアセンブリ言語環境にて自作言語を動かすという夢があるので実装しました。
シリーズ化します(断言)。
strtok
char *strtok(s, ct) strtokではctからの文字によって区切られるトークンがsの中で探される。
プログラミング言語C 第2版 314頁より引用
かみ砕いて言うなら、まず初めにsに区切りたい文字列、ctに区切り文字列を指定し、区切り文字によって区切られた文字列のポインタを返す、二回目以降はsにNULLを設定し呼び出すことで次々と区切られた文字列のポインタが渡されるような挙動をします。
もどきなので実装するものの区切り文字は1つしか指定できませんが、まあ自分はスペースが区切れれば良いので良いとします。
(2022/09/24追記 string.h
のstrtok
は区切り文字の間が空だったときそこを無視して次を読みに行こうとしますが、今回実装するものは間が空だったときNULL
で埋められた領域を返却するという挙動をします。コメント欄参照)
フローチャート
厳密なフローチャートではありませんが、プログラムと合わせてみていただければわかりやすいのではと思います。
プログラム
; int* strtok(int* sadr, char delimit)
; sadr(GR1) : src string adr, local_char
; delimit(GR2) : delimiter char
; ret(GR0) : token string adr
; l_temp(GR3) : local temporary reg
; l_sidx(GR4) : local string index reg
; l_bidx(GR5) : local buf index reg
;CONST
STCNONE DC 1 ; CONST 1
STCNNUL DC 0 ; CONST NULL
;VAR
STAIDX DC 0 ; StrTok Addr InDeX
STSSA DC 0 ; StrTok Src String Addr
STSTBF DS 256 ; StrTok Str Token BuF
STSTBFL DC 256 ; StrTok Str Token BuF Length
STFNL DC 0 ; StrTok Flag NulL
STRTOK RPUSH 1, 6
CPA GR1, STCNNUL ; if sadr = NULL
JZE STSANL ; then STSANL
LAD GR3, 0 ;
ST GR3, STAIDX ; STAIDX = 0
ST GR3, STFNL ; STFNL = 0
ST GR1, STSSA ; STSSA = sadr
JUMP STBFINI ; jump StrTok Buf INItialize
STSANL LD GR3, STSSA ;
CPA GR3, STCNNUL ; if STSSA = NULL
JNZ STBFINI ; else STBFINI
LD GR0, NULL ; ret NULL
JUMP STFIN ; jump STFIN
STBFINI LAD GR3, 0 ; l_temp = 0
LD GR4, STAIDX ; l_sidx = STAIDX
LAD GR5, 0 ; l_bidx = 0
STBFINL ST GR3, STSTBF, GR5 ; STSTBF[l_bidx] = l_temp
ADDA GR5, STCNONE ; l_bidx++
CPA GR5, STSTBFL ; if l_bidx = STSTBFL
JZE STLPHD ; then STLPHD
JUMP STBFINL ; jump STBFINL
STLPHD LAD GR5, 0 ; l_bidx = 0
STLPBD LD GR1, STSSA ; l_c = STSSA[l_sidx]
ADDA GR1, GR4 ;
LD GR1, 0, GR1 ;
CPA GR1, GR2 ; if l_c = delimit
JZE STADLMT ; then STADLMT
CPA GR1, STCNNUL ; if l_c = NULL
JZE STANL ; then STANL
ST GR1, STSTBF, GR5 ; STSTBF[l_bidx] = l_c
ADDA GR5, STCNONE ; l_bidx++
ADDA GR4, STCNONE ; l_sidx++
JUMP STLPBD ; jump STLPBD
STADLMT ADDA GR4, STCNONE ; l_sidx++
LAD GR0, STSTBF ; return STSTBF
JUMP STEND ; jump STEND
STANL LD GR3, STFNL ; if STFNL = NULL
CPA GR3, STCNNUL ;
JZE STAFNL ; then STAFNL
LAD GR0, 0 ; return 0
JUMP STEND ; jump STEND
STAFNL LAD GR3, 1 ; STFNL = TRUE
ST GR3, STFNL ;
LAD GR0, STSTBF ; return STSTBF
JUMP STEND ; jump STEND
STEND ST GR4, STAIDX ; STAIDX = l_sidx
STFIN RPOP 1, 6
RET
利用例
実際に動かしてみます。STRTOK
は省略します。
PGM START
LAD GR1, SRC
LAD GR2, ' '
CALL STRTOK
CALL PRTAD
LD GR1, NULL
LOOP CALL STRTOK
CPA GR0, NULL
JZE LOOPEND
CALL PRTAD
JUMP LOOP
LOOPEND RET
; int* strtok(int* sadr, char delimit)
; 省略
PRTAD OUT ='[', =1
RPUSH 0, 3
LAD GR2, 0
LAD GR3, '\n'
CPA GR0, =0
JZE PRTADER
PRTADLP LD GR1, 0, GR0
CPA GR1, NULL
JZE PRTADED
WRITE GR2, GR1
ADDA GR0, =1
JUMP PRTADLP
PRTADED RPOP 0, 3
OUT =']\n', =2
RET
PRTADER RPOP 0, 3
OUT =']0\n', =3
RET
SRC DC 'Hello world !\0'
NULL DC '\0'
END
strtok.fe
という名前で保存し実行してみると、
> python mlfe.py strtok.fe
[Hello]
[world]
[]
[!]
ちゃんと動いてますね、区切り文字が連続で存在する場所では返却値のポインタはヌル文字で埋め尽くされた領域のポインタを返し、ヌル文字が見つかったら返却値はヌルになってます。
おわりに
皆様も低レベルなプログラミング言語で、Cの標準ライブラリの機能を作ってみてはいかが?土日に暇つぶしにお勧めです。
それではさようなら。