MLFEでstrtokもどきを実装する

# はじめに
自作のアセンブリ言語環境にて自作言語を動かすという夢があるので実装しました。
シリーズ化します（断言）。

# strtok

```
char *strtok(s, ct) strtokではctからの文字によって区切られるトークンがsの中で探される。
```
プログラミング言語Ｃ 第２版 314頁より引用

かみ砕いて言うなら、まず初めにsに区切りたい文字列、ctに区切り文字列を指定し、区切り文字によって区切られた文字列のポインタを返す、二回目以降はsにNULLを設定し呼び出すことで次々と区切られた文字列のポインタが渡されるような挙動をします。

もどきなので実装するものの区切り文字は1つしか指定できませんが、まあ自分はスペースが区切れれば良いので良いとします。
(2022/09/24追記　`string.h`の`strtok`は区切り文字の間が空だったときそこを無視して次を読みに行こうとしますが、今回実装するものは間が空だったとき`NULL`で埋められた領域を返却するという挙動をします。コメント欄参照)

# フローチャート

厳密なフローチャートではありませんが、プログラムと合わせてみていただければわかりやすいのではと思います。

```mermaid
flowchart TD
    START(["START strtok"])
    END(["END strtok"])
    COND1{"Is argument sadr<br>is not NULL?"}
    COND2{"Is STSSA<br>already assigned?"}
    COND3{"Is l_c delimiter?"}
    COND4{"Is l_c NULL?"}
    COND5{"STFNL<br>Str Token Flag NulL"}
    PROCX1["l_sidx=0"]
    PROCX2["STFNL=false"]
    PROCX3["STSSA=sadr"]
    PROCNN["return NULL"]
    PROCBUF[["Token string buffer<br>Initialization"]]
    PROCGC["l_c=sadr[l_sidx]"]
    PROCNC1["STSTBF[l_bidx]=l_c"]
    PROCNC2["l_bidx++<br>l_sidx++"]
    PROCAD1["l_sidx++"]
    PROCAD2["return STSTBF"]
    PROCNL1["STFNL=true"]
    PROCNL2["return STSTBF"]
    PROCAN["return NULL"]

    START-->COND1
    COND1--Yes-->PROCX1
    COND1--No-->COND2
    COND2--Yes-->PROCBUF
    COND2--No-->PROCNN
    PROCNN-->END
    PROCX1-->PROCX2
    PROCX2-->PROCX3
    PROCX3-->PROCBUF
    PROCBUF-->PROCGC
    PROCGC-->COND3
    COND3--No-->COND4
    COND3--Yes-->PROCAD1
    PROCAD1-->PROCAD2
    PROCAD2-->END
    COND4--Yes-->COND5
    COND4--No-->PROCNC1
    COND5--false-->PROCNL1
    PROCNL1-->PROCNL2
    PROCNL2-->END
    COND5--true-->PROCAN
    PROCAN-->END
    PROCNC1-->PROCNC2
    PROCNC2------>PROCGC


```

# プログラム

```
; 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の標準ライブラリの機能を作ってみてはいかが？土日に暇つぶしにお勧めです。
それではさようなら。