最近のトラックバック

ウェブページ

2017年12月
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

他のアカウント

無料ブログはココログ

« 佐野千遥教授による、実数論の間違い、に関する質問の解答 | トップページ | 現行憲法前文の主語を「あなた」「私」に変えてみた »

2016年5月 3日 (火)

C言語で GOTO(整数型式) マクロ作ってスパゲッティプログラムを作ってみた

ストレスがたまったので、C言語(_Bool 型やブロックの中途で変数の定義など使用)で、スパゲッティ・プログラムを作ってみた。
GOTO(整数型式) マクロでジャンプし、 LABEL(整定数)マクロ に制御が移る。両方共展開するとひとつの文となる。GOTOマクロの引数は定数でなくともよい。
ついでなので、basic風味にしてみた。

内容は「Hellow, World!」出力です。

spaghetti.c
----------

LABEL(10)   REM("'Hello, World!' を出力する")
LABEL(20)   REM("注意: これはC言語ソースです!")
LABEL(30)   REM("i: アルファベットの何番目の文字を出力するか示す")
LABEL(40)   INT i;
LABEL(50)   REM("j: 200番のFORの添字、および、負のとき'l'を1回だけ出力するフラグ")
LABEL(60)   INT j;
LABEL(70)   REM("return_label: 擬似サブルーチンの戻り先")
LABEL(80)   INT return_label = 0;
LABEL(90)   REM("uppercase: 出力する文字が大文字のとき真");
LABEL(100)  BOOL uppercase = true;
LABEL(110)  REM("curr_ch: 出力文字")
LABEL(120)  CHAR curr_ch = '\0';
LABEL(130)  REM("'H'を出力")
LABEL(140)  gosub(2000);
LABEL(150)  FOR_TO(i, 0, 127) // FOR i=0 TO 127: 127に特に意味はない
LABEL(160)      IF !i THENG(2030)
LABEL(170)      ELSE_IF i==4 THEN
LABEL(180)          REM("4番目の文字eを出力したならば")
LABEL(190)          REM(" 'l' を2回出力")
LABEL(200)          FOR_DOWNTO(j,2,1) // FOR j=2 TO 1 STEP -1
LABEL(210)              REM(" 'l' を出力する");
LABEL(220)              REM("j<0 のときは'l'を1回出力する擬似ルーチンの入り口")
LABEL(230)              gosub(2090); // 2090 はreturn_labelを弄らないので270番はOK
LABEL(240)          NEXT
LABEL(250)          IF j<0 THEN
LABEL(260)              REM("230番を入り口とした擬似サブルーチンとみなし、戻る")
LABEL(270)              GOTO( return_label );
LABEL(280)          END_IF
LABEL(290)      END_IF
LABEL(300)      GOTO(2500);
LABEL(310)  NEXT
LABEL(320)  STOP
LABEL(1000) REM("文字 i 番目を出力するサブルーチン")
LABEL(1010) curr_ch = 'a' + i;
LABEL(1020) IF uppercase THEN curr_ch = toupper( curr_ch ); END_IF
LABEL(1030) putchar( curr_ch );
LABEL(1040) RETURN
LABEL(2000) REM("'H'を出力するサブルーチン")
LABEL(2010) LET i = 7;
LABEL(2020) GOTO((curr_label/1000 -1)*1000); // 実は GOTO(1000)
LABEL(2030) REM("iを変更し、'e'を出力して 160 に戻る   ");
LABEL(2040) LET uppercase = false;
LABEL(2050) LET i = 4;
LABEL(2060) LET curr_ch = 'a' + i;
LABEL(2070) putchar( curr_ch );
LABEL(2080) GOTO(160);
LABEL(2090) REM("'l'を出力するサブルーチン")
LABEL(2100) LET uppercase = false;
LABEL(2110) LET i = 11;
LABEL(2120) GOTO(1000);
LABEL(2500) REM("ow, Wo を出力する")
LABEL(2510) REM("nest: 次のサブルーチンの深さ")
LABEL(2520) INT nest = 0;
LABEL(2530) REM("oW, Wo を出力する再帰的サブルーチン")
LABEL(2540) switch( nest ){
LABEL(2550) REM("このブロック内では、LABEL番号は実は直上のswitchに関連付けられるが")
LABEL(2560) REM("nest の値とかぶらない")
LABEL(2570) case 0:         putchar('o');
LABEL(2580)                 nest++;
LABEL(2590)                 gosub(2530); // 再帰的呼び出し
LABEL(2600)                 putchar('o');
LABEL(2610)                 REM("nest==0 はgosubで呼ばれないことが前提なのでGOTO脱出")
LABEL(2620)                 GOTO(3000);
LABEL(2630)                 REM("NOT REACHED")
LABEL(2640)                 break;
LABEL(2650) case 1:         putchar('w');
LABEL(2660)                 nest++;
LABEL(2670)                 gosub(2530); // 再帰的呼び出し
LABEL(2680)                 putchar('W');
LABEL(2690)                 break;
LABEL(2700) default:        putchar(',');
LABEL(2710)                 putchar(' ');
LABEL(2720)                 break;
LABEL(2730) }
LABEL(2740) RETURN
LABEL(3000) REM("rld! を出力")
LABEL(3010) INT offset = 4;
LABEL(3020) GOTO(3000 + 100*offset)
LABEL(3100) REM("'!'+改行を出力して停止する場所へGOTO") // LABEL 3000 + 1*100
LABEL(3110) puts("!");
LABEL(3120) REM("ラベル 150 FOR文の終値より充分大きな値をiに入れる")
LABEL(3130) LET i = 1000;
LABEL(3140) GOTO(310)
LABEL(3200) REM("'d' を出力")    // LABEL 3000 * 2*100
LABEL(3210) LET i = 3;
LABEL(3220) LET uppercase = false;
LABEL(3230) gosub(1000);
LABEL(3240) LET offset-- ;
LABEL(3250) GOTO(3020)
LABEL(3300) REM("'l' を出力") // LABEL 3000 +3*100
LABEL(3310) LET j = -1;
LABEL(3320) LET offset-- ;
LABEL(3330) LET return_label = 3020; // 擬似サブルーチンの戻り先
LABEL(3340) GOTO(230)
LABEL(3400) REM("'r' を出力") // LABEL 3000 + 4*100
LABEL(3410) LET i = 17;
LABEL(3420) LET uppercase = false;
LABEL(3430) gosub(1000);
LABEL(3440) LET offset-- ;
LABEL(3450) GOTO(3020)
LABEL(3500) REM("NOT REACHED")
LABEL(3510) STOP
LABEL(3520) END

----------

出力結果

----------

Hellow, World!
ラベル 320 で停止しました。

----------

メインプログラム

spaghetti-main.c
----------

#include "spaghetti.h"
#define LOG_FILE_NAME "labels.log"

static FILE *log;

int gosub( int curr_label );

int main( void ){
    if(!(log=fopen(LOG_FILE_NAME,"w"))){
        fputs(LOG_FILE_NAME  " が開けませんでした\n", stderr);
        return EXIT_FAILURE;
    }
    int ret = gosub( 0 );
    fclose(log);
    return ret;
}


int gosub( int curr_label ){
    // curr_label: 現在のラベル番号。LABELマクロ展開部分で更新される。
    while( true ){
        switch( curr_label ){
            // 無限ループの中にswitch文を入れることで
            // GOTO(整数式)を実現
LABEL(0)    // 先頭の文
            {

// 邪悪な include
#include "spaghetti.c"

            }           
            break;
default :   fprintf(stderr,
                "GOTO または gosub で存在しないラベルが参照されました。%d\n",
                curr_label );
            fclose(log);
            return EXIT_FAILURE;
            // not reached
            break;
        }
nextlabel:  // switch からの脱出先.
        ;
    }
    // not reached
    return EXIT_FAILURE;
}

ヘッダーファイル(マクロなどの定義)
spaghetti.h
----------

 

#ifndef SPAGHETTI_H
#define SPAGHETTI_H

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>

#define STOP    {printf("ラベル %d で停止しました。\n",curr_label); \
                    fclose(log); exit(EXIT_SUCCESS);}
#define END     {printf("ラベル %d でENDに到達しました。\n",curr_label); \
                    fclose(log); exit(EXIT_SUCCESS);}
#define RETURN  return EXIT_SUCCESS;
#define REM(X)
#define LET

#define INT  static int
#define BOOL static bool
#define CHAR static char

#define LABEL(label) {case (label): curr_label = (label); \
            fprintf(log, "LABEL %d\n", curr_label);}
#define GOTO(label) {curr_label=(label); goto nextlabel;}

#define IF if(
#define THEN ){
#define ELSE }else{
#define ELSE_IF } else if(
#define END_IF }
#define THENG(label) ){ GOTO(label)

#define FOR_TO( cnt, init, toval ) for((cnt)=(init);(cnt)<= (toval);(cnt)++ ){
#define FOR_DOWNTO( cnt, init, toval ) for((cnt)=(init);(cnt)>= (toval);(cnt)-- ){
#define NEXT }

#endif // SPAGHETTI_H

 

制御の流れ(通過したラベル番号)
labels.log
----------

 

LABEL 0
LABEL 10
LABEL 20
LABEL 30
LABEL 40
LABEL 50
LABEL 60
LABEL 70
LABEL 80
LABEL 90
LABEL 100
LABEL 110
LABEL 120
LABEL 130
LABEL 140
LABEL 2000
LABEL 2010
LABEL 2020
LABEL 1000
LABEL 1010
LABEL 1020
LABEL 1030
LABEL 1040
LABEL 150
LABEL 160
LABEL 2030
LABEL 2040
LABEL 2050
LABEL 2060
LABEL 2070
LABEL 2080
LABEL 160
LABEL 180
LABEL 190
LABEL 200
LABEL 210
LABEL 220
LABEL 230
LABEL 2090
LABEL 2100
LABEL 2110
LABEL 2120
LABEL 1000
LABEL 1010
LABEL 1020
LABEL 1030
LABEL 1040
LABEL 240
LABEL 210
LABEL 220
LABEL 230
LABEL 2090
LABEL 2100
LABEL 2110
LABEL 2120
LABEL 1000
LABEL 1010
LABEL 1020
LABEL 1030
LABEL 1040
LABEL 240
LABEL 250
LABEL 290
LABEL 300
LABEL 2500
LABEL 2510
LABEL 2520
LABEL 2530
LABEL 2540
LABEL 2580
LABEL 2590
LABEL 2530
LABEL 2540
LABEL 2660
LABEL 2670
LABEL 2530
LABEL 2540
LABEL 2710
LABEL 2720
LABEL 2740
LABEL 2680
LABEL 2690
LABEL 2740
LABEL 2600
LABEL 2610
LABEL 2620
LABEL 3000
LABEL 3010
LABEL 3020
LABEL 3400
LABEL 3410
LABEL 3420
LABEL 3430
LABEL 1000
LABEL 1010
LABEL 1020
LABEL 1030
LABEL 1040
LABEL 3440
LABEL 3450
LABEL 3020
LABEL 3300
LABEL 3310
LABEL 3320
LABEL 3330
LABEL 3340
LABEL 230
LABEL 2090
LABEL 2100
LABEL 2110
LABEL 2120
LABEL 1000
LABEL 1010
LABEL 1020
LABEL 1030
LABEL 1040
LABEL 240
LABEL 250
LABEL 260
LABEL 270
LABEL 3020
LABEL 3200
LABEL 3210
LABEL 3220
LABEL 3230
LABEL 1000
LABEL 1010
LABEL 1020
LABEL 1030
LABEL 1040
LABEL 3240
LABEL 3250
LABEL 3020
LABEL 3100
LABEL 3110
LABEL 3120
LABEL 3130
LABEL 3140
LABEL 310
LABEL 320

« 佐野千遥教授による、実数論の間違い、に関する質問の解答 | トップページ | 現行憲法前文の主語を「あなた」「私」に変えてみた »

C/C++言語」カテゴリの記事

パソコン・インターネット」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/525300/63575766

この記事へのトラックバック一覧です: C言語で GOTO(整数型式) マクロ作ってスパゲッティプログラムを作ってみた:

« 佐野千遥教授による、実数論の間違い、に関する質問の解答 | トップページ | 現行憲法前文の主語を「あなた」「私」に変えてみた »