Написание интерпретатора схемы с помощью FPC: рекурсивные структуры данных

По сути, это вопрос о рекурсивных структурах данных в Паскале (FPC). Поскольку я хотел бы реализовать интерпретатор Scheme, как показано в главе 4 SICP, этот вопрос может быть актуален и для Schemers. :)

S-выражения должны быть представлены в виде тегированных данных. До сих пор я построил вариантную запись, которая представляет числа и пары. Надеюсь, код читабелен и не требует пояснений:

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.

Однако компиляция кода приводит к следующей ошибке:

$ 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

Очевидно, что в функции MakePair есть ошибка. Но пока не понимаю, что именно я делаю не так. Любая помощь приветствуется. :)


person Community    schedule 04.01.2013    source источник


Ответы (2)


Функция MakePair определяется следующим образом:

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

Обратите внимание, что он получает два указателя типа PScmObject. Затем вы называете это так:

MakePair(Test1, Test2);

Но Test1 и Test2 относятся к типу TScmObject. Таким образом, фактически переданные параметры несовместимы, как говорит компилятор.

Вместо этого вам нужно передать указатели на эти записи:

MakePair(@Test1, @Test2);

В долгосрочной перспективе вам нужно быть осторожным в отношении срока службы этих записей. Вам нужно будет выделить в куче и без сборки мусора, я подозреваю, что вы попадете в мир боли, пытаясь отслеживать, кому принадлежат записи. Возможно, вы могли бы рассмотреть возможность использования подсчета ссылок интерфейса для управления временем жизни.

person David Heffernan    schedule 04.01.2013

Процедура ожидает указатель на запись, а не саму запись.

Вы можете использовать оператор @ (at) в точке вызова, чтобы создать указатель на лету на запись и, таким образом, выполнить проверку типа компилятора:

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.
person jachguate    schedule 04.01.2013