SICP 読み (380) 5.5 翻訳系

問題 5.51

gauche.h の一部を流用する方向です。相当ズルだと思うんですが、検討な軌跡は全部ログ投入な予定ですので、それで勘弁して下さひ (誰
あと、方向性としては

  • とりあえず gc は盛り込まない。別途 BohemGC を使う形にするかも。
  • gauche.h からは
    • BASIC TYPE
    • IMMEDIATE OBJECTS
    • BOOLEAN
    • FIXNUM
    • CHARACTERS
    • HEAP ALLOCATED OBJECTS
    • CLASS
    • PAIR AND LIST
    • STRING
    • SYMBOL
    • NUMBER
    • UTILITY

って下の方はヤリ杉感満点だし。とりあえず上から試験書きながらヤッツケる方向で。見ていきながら rudimentary implementation な範疇超えてると判断した時点でスルーか。 (を

もう少し準備しつつ

環境整備してから着手、って準備が長いよ。とりあえず、試験なソースは別ディレクトリにしておいた方が良さげ。Makefile も作らないと。どこかのチュートリアルに autoconf/automake なソレが出ておりましたが、面倒臭いのでこのあたりはスルー。
基本的に LinuxgccGNU make で cunit が前提という無理矢理な環境。とりあえず試験なディレクトリを作成してその中で動く Makefile を書こう。微妙ですが、とりあえず動いたのが以下

TARGET=test
OBJS=main.o
CFLAGS=-I..
LDFLAGS=-lcunit

all: $(TARGET)
	@./$(TARGET)

$(TARGET): $(OBJS)
	$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS)

clean:
	rm -rf $(TARGET) $(OBJS) *~

次はその下に Makefile 作って、test なターゲットのみ。

UT: 
	cd test && $(MAKE)

clean:
	@rm -rf *~ && cd test && $(MAKE) clean

とりあえず今はこれだけで OK か。以下なカンジです。

$ ls
Makefile  gauche.h  gauche.h.ORG  test/
$ make
cd test && make
make[1]: ディレクトリ `/home/rms/1.programming/2.boutC/8.cunit/test' に入ります
cc -I..   -c -o main.o main.c
cc -o test main.o -lcunit


     CUnit - A Unit testing framework for C - Version 2.1-0
     http://cunit.sourceforge.net/



--Run Summary: Type      Total     Ran  Passed  Failed
               suites        1       1     n/a       0
               tests         1       1       1       0
               asserts       6       6       6       0
make[1]: ディレクトリ `/home/rms/1.programming/2.boutC/8.cunit/test' から出ます
$ make clean
make[1]: ディレクトリ `/home/rms/1.programming/2.boutC/8.cunit/test' に入ります
rm -rf test main.o *~
make[1]: ディレクトリ `/home/rms/1.programming/2.boutC/8.cunit/test' から出ます
$

ひさびさに make をサワるんで微妙さ満点。あとは試験なソースを分けるソレで準備完了かなぁ。ソースが増える度に Makefile に手を入れる、とゆーのがちょっとナニですが、そのあたりはおいおい、とゆー事で。
で、現時点でソースツリーが以下

$ find
.
./gauche.h
./test
./test/main.c
./test/Makefile
./test/test-PRIMARY_TAG.c
./test/schemeTest.h
./Makefile
./gauche.h.ORG
$

現時点の test 配下のソースが以下。最初は main.c

#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>

#include "schemeTest.h";

int main(void)
{

  CU_pSuite sample_suite;

  CU_initialize_registry();
  sample_suite = CU_add_suite("Sort", NULL, NULL);
  CU_add_test(sample_suite, "gauche.h (SCM_PTRP)", test_gauche_SCM_PTRP);

  CU_basic_run_tests();

  CU_cleanup_registry();

  return 0;
}

schemeTest.h が以下

void test_gauche_SCM_PTRP(void);

test-PRIMARY_TAG.c が以下

#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>

#include "gauche.h"

void test_gauche_SCM_PTRP(void)
{
  CU_ASSERT_TRUE(SCM_PTRP(0));
  CU_ASSERT_FALSE(SCM_PTRP(1));
  CU_ASSERT_FALSE(SCM_PTRP(2));
  CU_ASSERT_FALSE(SCM_PTRP(3));
  CU_ASSERT_TRUE(SCM_PTRP(4));
}

試験なソースを追加する度に Makefile の修正が必要だし、試験な関数が増える度にヘッダと main.c の修正が必要になるんですが、これは仕方が無いのかなぁ。
とりあえずこれで準備完了と見て、以降で確認作業着手です。

SCM_TAG な試験

も追加。

void test_gauche_SCM_TAG(void)
{
  CU_ASSERT_EQUAL(0, SCM_TAG(0));
  CU_ASSERT_EQUAL(1, SCM_TAG(1));
  CU_ASSERT_EQUAL(2, SCM_TAG(2));
  CU_ASSERT_EQUAL(3, SCM_TAG(3));
  CU_ASSERT_EQUAL(0, SCM_TAG(4));
}

これ、コメントとか入れられんのかな。次は即値なマクロの試験を。楽に進捗すると楽しくなるからイケマセン。

IMMEDIATE OBJECTS

調子に乗ってどんどん盛り込み。とりあえず以下に test-IMMEDIATES.c を

#include <CUnit/CUnit.h>

#include "gauche.h"

void test_gauche_SCM_IMMEDIATEP(void)
{
  CU_ASSERT_FALSE(SCM_IMMEDIATEP(0));
  CU_ASSERT_FALSE(SCM_IMMEDIATEP(1));
  CU_ASSERT_FALSE(SCM_IMMEDIATEP(2));
  CU_ASSERT_FALSE(SCM_IMMEDIATEP(3));
  CU_ASSERT_FALSE(SCM_IMMEDIATEP(4));
  CU_ASSERT_FALSE(SCM_IMMEDIATEP(5));
  CU_ASSERT_TRUE(SCM_IMMEDIATEP(6));
  CU_ASSERT_FALSE(SCM_IMMEDIATEP(7));

  CU_ASSERT_TRUE(SCM_IMMEDIATEP(SCM_FALSE));
  CU_ASSERT_TRUE(SCM_IMMEDIATEP(SCM_TRUE));
  CU_ASSERT_TRUE(SCM_IMMEDIATEP(SCM_NIL));
  CU_ASSERT_TRUE(SCM_IMMEDIATEP(SCM_EOF));
  CU_ASSERT_TRUE(SCM_IMMEDIATEP(SCM_UNDEFINED));
  CU_ASSERT_TRUE(SCM_IMMEDIATEP(SCM_UNBOUND));
}

void test_gauche_SCM_ITAG(void)
{
  CU_ASSERT_EQUAL(0, SCM_ITAG(SCM_FALSE));
  CU_ASSERT_EQUAL(1, SCM_ITAG(SCM_TRUE));
  CU_ASSERT_EQUAL(2, SCM_ITAG(SCM_NIL));
  CU_ASSERT_EQUAL(3, SCM_ITAG(SCM_EOF));
  CU_ASSERT_EQUAL(4, SCM_ITAG(SCM_UNDEFINED));
  CU_ASSERT_EQUAL(5, SCM_ITAG(SCM_UNBOUND));
}

void test_gauche_SCM__MAKE_ITAG(void)
{
  int value [] = {6, 22, 38, 54, 70, 86};
  int i;

  for(i = 0; i < 6; i++) {
    CU_ASSERT_EQUAL(value[i], SCM__MAKE_ITAG(i));
    CU_ASSERT_TRUE(SCM_IMMEDIATEP(value[i]));
  }
}

void test_gauche_SCM_IMMEDIATEP_2(void)
{
  CU_ASSERT_TRUE(SCM_FALSEP(SCM_OBJ(SCM__MAKE_ITAG(0))));
  CU_ASSERT_TRUE(SCM_TRUEP(SCM_OBJ(SCM__MAKE_ITAG(1))));
  CU_ASSERT_TRUE(SCM_NULLP(SCM_OBJ(SCM__MAKE_ITAG(2))));
  CU_ASSERT_TRUE(SCM_EOFP(SCM_OBJ(SCM__MAKE_ITAG(3))));
  CU_ASSERT_TRUE(SCM_UNDEFINEDP(SCM_OBJ(SCM__MAKE_ITAG(4))));
  CU_ASSERT_TRUE(SCM_UNBOUNDP(SCM_OBJ(SCM__MAKE_ITAG(5))));
}

BOOLEAN

次は test-BOOLEAN.c

#include <CUnit/CUnit.h>

#include "gauche.h"

void test_gauche_SCM_BOOLP(void)
{
  CU_ASSERT_TRUE(SCM_BOOLP(SCM_OBJ(6)));
  CU_ASSERT_TRUE(SCM_BOOLP(SCM_OBJ(22)));
  CU_ASSERT_TRUE(SCM_BOOLP(SCM_TRUE));
  CU_ASSERT_TRUE(SCM_BOOLP(SCM_FALSE));
  CU_ASSERT_FALSE(SCM_BOOLP(0));
}

void test_gauche_SCM_MAKE_BOOL(void)
{
  CU_ASSERT_TRUE(SCM_BOOLP(SCM_MAKE_BOOL(TRUE)));
  CU_ASSERT_TRUE(SCM_BOOLP(SCM_MAKE_BOOL(FALSE)));
  CU_ASSERT_TRUE(SCM_FALSEP(SCM_MAKE_BOOL(FALSE)));
  CU_ASSERT_TRUE(SCM_TRUEP(SCM_MAKE_BOOL(TRUE)));
}

void test_gauche_SCM_EQ(void)
{
  CU_ASSERT_EQUAL(TRUE, SCM_EQ(1, 1));
  CU_ASSERT_EQUAL(FALSE, SCM_EQ(1, 2));
}

規模がまだ小さいので自分メモで Makefile とかヘッダとかその他モロモロを以下に。まず test/Makefile が以下

TARGET=test
OBJS=main.o test-PRIMARY_TAG.o test-IMMEDIATES.o test-BOOLEAN.o
CFLAGS=-I.. -Wall
LDFLAGS=-lcunit

all: $(TARGET)
	@./$(TARGET)

$(TARGET): $(OBJS)
	$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS)

clean:
	rm -rf $(TARGET) $(OBJS) *~

つぎは main.c

#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>

#include "schemeTest.h"

int main(void)
{
  CU_pSuite gauche_h_suite;

  CU_initialize_registry();

  gauche_h_suite = CU_add_suite("gauche.h", NULL, NULL);
  CU_add_test(gauche_h_suite, "SCM_TAG", test_gauche_SCM_TAG);
  CU_add_test(gauche_h_suite, "SCM_PTRP", test_gauche_SCM_PTRP);
  CU_add_test(gauche_h_suite, "SCM_IMMEDIATEP", test_gauche_SCM_IMMEDIATEP);
  CU_add_test(gauche_h_suite, "SCM_ITAG", test_gauche_SCM_ITAG);
  CU_add_test(gauche_h_suite, "SCM__MAKE_ITAG", test_gauche_SCM__MAKE_ITAG);
  CU_add_test(gauche_h_suite, "SCM_IMMEDIATEP 2", test_gauche_SCM_IMMEDIATEP_2);
  CU_add_test(gauche_h_suite, "SCM_BOOLP", test_gauche_SCM_BOOLP);
  CU_add_test(gauche_h_suite, "SCM_MAKE_BOOL", test_gauche_SCM_MAKE_BOOL);
  CU_add_test(gauche_h_suite, "SCM_EQ", test_gauche_SCM_EQ);

  CU_basic_run_tests();

  CU_cleanup_registry();

  return 0;
}

最後に schemeTest.h

void test_gauche_SCM_TAG(void);
void test_gauche_SCM_PTRP(void);
void test_gauche_SCM_IMMEDIATEP(void);
void test_gauche_SCM_ITAG(void);
void test_gauche_SCM__MAKE_ITAG(void);
void test_gauche_SCM_IMMEDIATEP_2(void);
void test_gauche_SCM_BOOLP(void);
void test_gauche_SCM_MAKE_BOOL(void);
void test_gauche_SCM_EQ(void);

現時点で make したらこんなカンジ

$ find
.
./gauche.h
./test
./test/main.c
./test/test-PRIMARY_TAG.c
./test/Makefile
./test/schemeTest.h
./test/test-IMMEDIATES.c
./test/test-BOOLEAN.c
./Makefile
./gauche.h.ORG
$ make
cc -I.. -Wall   -c -o main.o main.c
cc -I.. -Wall   -c -o test-PRIMARY_TAG.o test-PRIMARY_TAG.c
cc -I.. -Wall   -c -o test-IMMEDIATES.o test-IMMEDIATES.c
cc -I.. -Wall   -c -o test-BOOLEAN.o test-BOOLEAN.c
cc -o test main.o test-PRIMARY_TAG.o test-IMMEDIATES.o test-BOOLEAN.o -lcunit


     CUnit - A Unit testing framework for C - Version 2.1-0
     http://cunit.sourceforge.net/



--Run Summary: Type      Total     Ran  Passed  Failed
               suites        1       1     n/a       0
               tests         9       9       9       0
               asserts      59      59      59       0
$

その内ここで使ってる gauche.h も貼りますが、現時点で BOOLEAN な SCM_EQ マクロまでの試験が終了してます。で、以下の

extern ScmObj Scm_EqP(ScmObj x, ScmObj y);
extern ScmObj Scm_EqvP(ScmObj x, ScmObj y);
extern ScmObj Scm_EqualP(ScmObj x, ScmObj y);

定義は別途、とゆー事でなるべくカンニングせずに作ってみたいな。eq? と eqv? と equal? の違いって何だったっけ。