● StrCalc BASIC の実装

4.ソースコードの読み込みと実行

実際に処理する BASIC ソースコードをメモリ上に読み込み、関数StrCalc_BASIC を実行します。

BASICPARAM構造体のメンバpsource にソースコードが格納されているバッファのアドレス、src_size にはソースコードの文字数を設定します。
例1. 文字列定数を指定した場合

#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include "sc39.h"

int __stdcall   usr_print(PSTRCALC_PARAM prm,char *arg,int len,void *param)
{
    int         ret;

    prm->str = arg;
    if ((ret = StrCalc(prm)) != STATUS_NORMAL)
        return ret;

    printf("%.16G\n",prm->ans);

    return STATUS_NORMAL;
}

USERSTATEMENT       table[] = {
    { "print", 5, usr_print, NULL }
};

void        basic_setting(PBASICPARAM pbas)
{
    pbas->random_seed = (unsigned)time(NULL);
    pbas->stackcnt_if = 16;
    pbas->stackcnt_for = 16;
    pbas->stackcnt_gosub = 16;
    pbas->b_localparam = 1;
    pbas->b_ignore_subcnt = 0;
    pbas->b_remark_colontype = 0;
    pbas->b_remark_ignorestr = 0;
    pbas->b_check_dup_label = 1;
    pbas->b_naked_us = 0;
    pbas->b_us_check = 0;
    pbas->b_removespace = 0;
    pbas->b_srand = 1;

    pbas->fprologue = NULL;
    pbas->fevery = NULL;
    pbas->fepilogue = NULL;
    pbas->proc_exparam = NULL;

    pbas->psubst = NULL;
    pbas->subst_exparam = NULL;

    pbas->pus = table;
    pbas->uscnt = sizeof(table) / sizeof(USERSTATEMENT);
}

void    main(void)
{
    STRCALC_PARAM       sc;
    BASICPARAM          bas;
    int                 ret;

    /* StrCalc環境の初期化 */
    if (!StrCalc_init(&sc)){
        printf("初期化に失敗\n");
        return;
    }

    basic_setting(&bas);

    bas.psource = "\
        for i = 0 to 10\n\
            @print i\n\
        next\
    ";
    bas.src_size = strlen(bas.psource); /* ヌル分を含まない事に注意 */

    /* StrCalc BASIC の実行 */
    ret = StrCalc_BASIC(&sc,&bas);

    /* StrCalc環境の終了 */
    StrCalc_term(&sc);

    if (ret != STATUS_NORMAL)
        printf("line : %lu     error.   %d\n",bas.stop_lineno,ret);
}
例1ソースファイル(zip)

ソースコードを格納するバッファは、BASIC 処理が終了するまで存在しているものであれば、どのような種類のものでもかまいません。
例1 では文字列定数を渡しています。サイズが大きなソースコードを実行する用途には向いていませんが、ちょっとした実験等を行う場合は、このように簡単に済ます事ができます。

例2. テキストファイルを読み込む
    (関数usr_print 、basic_setting は例1と同じ)

/* ファイルサイズ分のメモリを確保し、テキストを読み込む */
char    *load_src(char *filename,unsigned long *psize)
{
    FILE        *fp;
    char        *ptr;
    size_t      ret,size;

    if ((fp = fopen(filename,"rb")) == NULL)
        return NULL;

    if ((long)(size = _filelength(_fileno(fp))) <= 0 ||
        (ptr = (char *)malloc(size + 1)) == NULL)
    {
        fclose(fp);
        return NULL;
    }
    *psize = size;

    ret = fread(ptr,1,size,fp);
    fclose(fp);

    if (ret < size){
        free(ptr); return NULL;
    }

    ptr[size] = 0;
    return ptr;
}

#define FILENAME            "testbas.txt"

void    main(void)
{
    STRCALC_PARAM       sc;
    BASICPARAM          bas;
    int                 ret;

    if (!StrCalc_init(&sc)){
        printf("初期化に失敗\n");
        return;
    }

    basic_setting(&bas);

    if ((bas.psource = load_src(FILENAME,(size_t *)&bas.src_size)) == NULL){
        StrCalc_term(&sc);
        printf("ファイルの読み込みに失敗\n");
        return;
    }

    ret = StrCalc_BASIC(&sc,&bas);

    StrCalc_term(&sc);
    free(bas.psource);

    if (ret != STATUS_NORMAL)
        printf("line : %lu     error.   %d\n",bas.stop_lineno,ret);
}
この例では、ヒープを確保し、そこにテキストファイルの内容を読み込みます。
次の内容のテキストファイルを指定すると、例1 と同じ結果になります。

    for i = 0 to 10
        @print i
    next
例2ソースファイル(zip)