Writing a Scheme interpreter with FPC: Recursive d

2019-03-31 10:55发布

问题:

Essentially, this is a question about recursive data structures in Pascal (FPC). As I would like to implement a Scheme interpreter like it is shown in SICP chapter 4, this question may be relevant for Schemers as well. :)

S-expressions shall be represented as tagged data. So far, I have constructed a variant record, which represents numbers and pairs. Hopefully the code is readable and self-explanatory:

program scheme;

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

var
   Test1: TScmObject;
   Test2: TScmObject;
   Test3: TScmObject;

function MakeFixnum(x: integer): TScmObject;
var
   fixnum: TScmObject;
begin
   fixnum.ScmObjectTag := ScmFixnum;
   fixnum.ScmObjectFixnum := x;
   MakeFixnum := fixnum;
end;

function MakePair(car, cdr: PScmObject): TScmObject;
var
   pair: TScmObject;
begin
   pair.ScmObjectTag := ScmPair;
   pair.ScmObjectCar := car;
   pair.ScmObjectCdr := cdr;
   MakePair := pair;
end;

begin
   Test1 := MakeFixnum(7);
   writeln('Test1, Tag: ', Test1.ScmObjectTag,
           ', Content: ', Test1.ScmObjectFixnum);
   Test2 := MakeFixnum(9);
   writeln('Test2, Tag: ', Test2.ScmObjectTag,
           ', Content: ', Test2.ScmObjectFixnum);
   Test3 := MakePair(Test1, Test2);
end.

However, compiling the code yields an error as follows:

$ fpc scheme.pas
(...)
Compiling scheme.pas
scheme.pas(43,34) Error: Incompatible type for arg no. 2: Got "TScmObject", expected "PScmObject"
scheme.pas(45) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted

It is obvious that there is an error in the function MakePair. But I do not understand yet what exactly I am doing wrong. Any help is appreciated. :)

回答1:

The MakePair function is defined like this:

function MakePair(car, cdr: PScmObject): TScmObject;

Note that it receives two pointers of type PScmObject. You then call it like this:

MakePair(Test1, Test2);

But Test1 and Test2 are of type TScmObject. So the actual parameters passed are not compatible, just as the compiler says.

You need to pass pointers to these records instead:

MakePair(@Test1, @Test2);

In the longer term you are going to need to be careful about the lifetime of these records. You'll need to allocate on the heap and without garbage collection I suspect that you'll enter a world of pain trying to keep track of who owns the records. Perhaps you could consider using interface reference counting to manage lifetime.



回答2:

The procedure is expecting a pointer to the record, and not the record itself.

You can use the @ (at) operator, at the call point, to create a pointer on the fly to the record, and thus satisfy the compiler type check:

begin
   Test1 := MakeFixnum(7);
   writeln('Test1, Tag: ', Test1.ScmObjectTag,
           ', Content: ', Test1.ScmObjectFixnum);
   Test2 := MakeFixnum(9);
   writeln('Test2, Tag: ', Test2.ScmObjectTag,
           ', Content: ', Test2.ScmObjectFixnum);
   Test3 := MakePair(@Test1, @Test2);
end.