Writing a Scheme interpreter with FPC: Recursive data structures

delphi, freepascal, pascal, scheme, sicp

Solution

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.

Problem

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. :)

Original source