We All Get Old - Naskin Diary

Yet Another My Life On The Web

独自インタープリタに挑戦:#2.1 変数の保存

今回は変数の保存に挑戦。


変数は「var 変数名 = 値;」で宣言。

var age = 20;
var name = "naskin";

年齢は16進数で換算><
変数の型宣言(int,double,char,etc...)はなしの方向で。
「=」以降(空白除く)から「;」までの文字列を値として取り扱う。


使用する時は変数名に$をつけて使用。

print "my name is " $name ".";
print "I'am " $age "years old.";


スクリプトで宣言された変数は構造体を使った連結リストで保持。

struct VAR {
    struct VAR *next;    // 次の変数へのポインタ
    char *name;          // 変数名
    char *value;         // 値
} *v_header;             // 連結リストの先頭を指すポインタ

配列じゃなくて構造体とポインタを使う事でメモリが許す限り変数を保持できる(はず)。


構造体と連結リストの学習は以下の書籍を参考。

定本 Cプログラマのためのアルゴリズムとデータ構造 (SOFTBANK BOOKS)

定本 Cプログラマのためのアルゴリズムとデータ構造 (SOFTBANK BOOKS)


スクリプト例

var age = 20;
var name = "naskin";

print "my name is " $name ".";
print "I'am " $age "years old.";

実行例

my name is naskin. 
I'am 20years old. 

ハマリポイント

文字列から取り出した変数名と値を保存するときにstrtok関数が指す変数目と値のポインタを連結リストに渡してしまい、うまく保存出来ない目にあった。
「var 変数名 = 値;」をstrtokで変数名と値に分ける処理。

    char *name, *value;

    for (token = strtok(cmd, " "); token; token = strtok(NULL, " =\"\n;")) {
        if (strcmp(token, "var") != 0) {
            if (i == 0) {
                if ((name = malloc(strlen(token))) != NULL) {
                    memcpy(name, token, strlen(token));
                    i++;
                } else {
                    perror("var:malloc");
                    exit(1);
                }
            } else {
                if ((value = malloc(strlen(token))) != NULL) {
                    memcpy(value, token, strlen(token));
                } else {
                    perror("var:malloc");
                    exit(1);
                }
            }
        }
    }

ここで「name」と「value」に「token」のポインタをそのまま渡していた事で2回目の変数処理が行われたときにポインタがおかしくなっていた。きちんと「name」と「value」のメモリを確保してから「token」のポインタを渡さないとダメでした。。。やっぱりポインタの理解が弱い。

次回

print文で変数を表示する処理に挑戦。

環境

OS:Mac OS X 10.5.8
gcc:gcc version 4.0.1

ソース

ソース(myscript.c)をmain本体(myscript.c)とコマンド毎(var.c、print.c)に分割。
(print.c は 2.2で)


myscript.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "myscript_funcs.h"

#define LINELEN 1024

int main(int argc, char **argv) {
	FILE *sfp;
	char buf[LINELEN], cmd[LINELEN], *token, *word;
	int i, j,  buflen;

	if (argc != 2) {
		fprintf(stderr, "usage: %s <file>\n", argv[0]);
		exit(1);
	}

	if ((sfp = fopen(argv[1], "r")) == NULL) {
		fprintf(stderr, "script %s not found\n", argv[1]);
		exit(1);
	}

	if ((v_header = malloc(sizeof(struct VAR))) == NULL) {
		perror("myscript:malloc");
		exit(1);
	}

	while (fgets(buf, sizeof(buf), sfp) != NULL) {
		// 先頭の空白位置を取得
		i = (int)strspn(buf, "\t ");
		j = 0;
		buflen = strlen(buf);
		for (; i < buflen; i++, j++) {
			// 読込み行をコピー
			cmd[j] = buf[i];
			if (buf[i+1] == ";") {
				break;
			}
		}

		// コマンド行の処理
		if (!strncmp(cmd, "print", 5)) {
			print(cmd);
		}

		if (!strncmp(cmd, "var", 3)) {
			var(cmd);
		}
	}

	fclose(sfp);

	return 0;
}


var.c

#include "myscript_funcs.h"

int var(char *cmd) {
    char *token, *name, *value;
	int i = 0;
	struct VAR *p, *q;

    for (token = strtok(cmd, " "); token; token = strtok(NULL, " =\"\n;")) {
        if (strcmp(token, "var") != 0) {
			if (i == 0) {
				if ((name = malloc(strlen(token))) != NULL) {
					memcpy(name, token, strlen(token));
					i++;
				} else {
					perror("var:malloc");
					exit(1);
				}
			} else {
				if ((value = malloc(strlen(token))) != NULL) {
					memcpy(value, token, strlen(token));
				} else {
					perror("var:malloc");
					exit(1);
				}
			}
		}
    }

	if (v_header->next == NULL) {
		// 最初の変数リスト
		if ((q = malloc(sizeof(struct VAR))) != NULL) {
			q->name = name;
			q->value = value;
			q->next = NULL;
			v_header->next = q;
		} else {
			perror("var:malloc");	
		}
	} else {
		p = v_header->next;
		while (p->next != NULL) {
			p = p->next;
		}

		if ((q = malloc(sizeof(struct VAR))) != NULL) {
			q->name = name;
			q->value = value;
			q->next = NULL;
			p->next = q;
		} else {
			perror("var:malloc");	
		}
	}

// TODO 変数リストのダンプ処理を実装
	if (DEBUG) {
		for (p = v_header->next; p != NULL; p = p->next) {
			printf("DEBUG:p->name:'%s'\nDEBUG:p->value:'%s'\n", p->name, p->value);
		}
	}

    return 0;
}


myscript_funcs.h(構造体と関数の宣言)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debug.h"

#define VALNAMELEN 256		/* 変数名の長さ */

struct VAR {
	struct VAR *next;
	char *name;
	char *value;
} *v_header;

int print(char *);
int var(char *);


makefile

OBJS = myscript.o print.o var.o
CC = /usr/bin/cc

myscript : ${OBJS}
	${CC} -o $@ ${OBJS}

print.o : print.c
	${CC} -c $?

var.o : var.c
	${CC} -c $?

myscript.o : myscript.c
	${CC} -c $?

clean:
	/bin/rm -f myscript ${OBJS}

コンパイル

$ make
/usr/bin/cc -c myscript.c
myscript.c: In function ‘main’:
myscript.c:36: warning: comparison between pointer and integer
/usr/bin/cc -c print.c
/usr/bin/cc -c var.c
/usr/bin/cc -o myscript myscript.o print.o var.o

コンパイラの警告の消し方がわからへん><
配列同士の比較をstrcmpに変えても消えんし。。。

実行

$ cat ./value.mys 
var age = 20;
var name = "naskin";

print "my name is " $name ".";
print "I'am " $age "years old.";

$ ./myscript value.mys 
my name is naskin. 
I'am 2years old. 
$