4

歴史家である私にとって、FPC で Scheme インタープリターを書くことは、すでに最初の段階にあり、深刻な作業であることがわかりました。:) 私は、C でそれを行う方法を示したPeter Michaux のブログをフォローしています (Pascal に役立つ可能性があるAdaへの翻訳もあります)。

Michaux の作品 (v 0.1) からの C の次の 2 つの関数を考えてみましょう。

object *alloc_object(void) {
    object *obj;

    obj = malloc(sizeof(object));
    if (obj == NULL) {
        fprintf(stderr, "out of memory\n");
        exit(1);
    }
    return obj;
}

object *make_fixnum(long value) {
    object *obj;

    obj = alloc_object();
    obj->type = FIXNUM;
    obj->data.fixnum.value = value;
    return obj;
}

私が理解している限り (C の基本的な読み取り知識)、コンストラクターmake_fixnumは構造体 (fixnum 型のタグ付きデータ) へのポインターを返します。構築されたオブジェクトの場合、メモリを割り当てる必要があります(昨日のポイントについて@David Heffernanに感謝します)。

これは、これまでの FPC への私の翻訳であり、エラーなしでコンパイルされます。

program scheme;

type
   TTag = (ScmFixnum);
   PScmObject = ^TScmObject;
   TScmObject = record
      case ScmObjectTag: TTag of
         ScmFixnum: (ScmObjectFixnum: integer);
      end;

var Test: PScmObject = nil;

procedure AllocateObject(x: PScmObject);
begin
    new(x);
end;

function MakeFixnum(x: integer): PScmObject;
var
   fixnum: PScmObject = nil;
begin
   AllocateObject(fixnum);
   fixnum^.ScmObjectTag := ScmFixnum;
   fixnum^.ScmObjectFixnum := x;
   MakeFixnum := fixnum;
end;

begin
   Test := MakeFixnum(1);
   writeln(Test^.ScmObjectTag);
   writeln(Test^.ScmObjectFixnum);
end.

でも...:

$ ./test 
Runtime error 216 at $080480DD
  $080480DD
  $08048117
  $08063873

ポインターの使用方法と参照方法に重大な欠陥があると思われます。

このポインタとメモリがどのように機能するかを理解するのを手伝ってくれた人に感謝します (FAQ や論文などへの参照も大歓迎です)。

4

1 に答える 1

6

AllocateObject 関数が間違っています。変数 x に新しいオブジェクトを作成していますが、値によって呼び出されるため、作成されたオブジェクトを呼び出し元の関数に渡しません。呼び出し規約を変更すると、次のように機能します。

 procedure AllocateObject(out x: PScmObject);
 begin 
    new(x);
 end;

デバッガーで fixnum 変数を見ると、nil のままであることがわかります。



あなたの質問とは関係ありませんが、インタプリタでレコードを使用するのは良い考えではないと思います。それはすぐにメモリ管理の悪夢に変わります (少なくとも、20 kloc に近づいたときに私が書いたインタープリターでそれが起こり、次のようにレコードを置き換えなければなりませんでした:)

あなたの記録の代わりに

 PScmObject = ^TScmObject;
 TScmObject = record
    case ScmObjectTag: TTag of
       ScmFixnum: (ScmObjectFixnum: integer);
    end;

次のようなクラスを使用できます。

TScmObject = class()
  function Tag: TTag; virtual; abstract;
  function Fixnum: integer; virtual; abstract;
end;
TScmObjectFixNum = class(TScmObject)
  function Tag: TTag; override;
  function Fixnum: integer; override;
private
  value: integer;
end;

function TScmObjectFixNum.Tag: TTag;
begin
  result := ScmFixnum; 
end;
function TScmObjectFixNum.Fixnum: integer; 
begin
  result := value; 
end;

次に、簡単に作成します

 var x: TScmObject;
 x := TScmObjectFixNum.create() ;
 if x.tag = scmfixnum (* or x is TScmObjectFixNum *) then
    ... x.scmfixnum ...
 x.free

スキームの実装に循環参照がない場合は、インターフェイスを使用することもできます。次に、参照がカウントされ、自動的に解放されます。

IScmObject = interface
  function Tag: TTag;
  function Fixnum: integer;
end;
TScmObject = class(TInterfacedObject, IScmObject)
  function Tag: TTag; virtual; abstract;
  function Fixnum: integer; virtual; abstract;
end;
TScmObjectFixNum = class(TScmObject)
  function Tag: TTag; override;
  function Fixnum: integer; override;
private
  value: integer;
end;



 var x: IScmObject;
 x := TScmObjectFixNum.create() ;
 if x.tag = scmfixnum (* or x is TScmObjectFixNum *) then
    ... x.scmfixnum ...
 //x.free no longer necessary (or allowed)!
于 2013-01-05T22:14:23.117 に答える