Написание интерпретатора Scheme с использованием FPC: распределение и указатели

Будучи историком, написание интерпретатора Scheme в FPC уже на первом этапе оказывается для меня серьезной задачей.:) Я слежу за блогом Питера Мишо, который показал, как это сделать на C (есть также перевод на Ada, который может быть полезен для Pascal).

Рассмотрим эти две функции в C из работы Мишо (v 0.1):

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;
}

Насколько я понимаю (просто базовые знания по чтению на С), конструктор 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

Я подозреваю, что есть серьезный недостаток в том, как я использую и ссылаюсь на указатели.

Большое спасибо всем, кто помогает мне понять, как работает этот указатель и память (также приветствуются ссылки на часто задаваемые вопросы, статьи и т. Д.).

1 ответ

Решение

Ваша функция AllocateObject неверна. Он создает новый объект в переменной x, но не передает созданный объект вызывающей функции, поскольку он вызывается по значению. Если вы измените соглашение о вызовах, оно работает:

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

Вы можете увидеть, если вы посмотрите на переменную fixnum в отладчике, она останется нулевой.



Не связанный с вашим вопросом, я не думаю, что это хорошая идея использовать записи в переводчике. Скоро это превращается в кошмар управления памятью (по крайней мере, это произошло в интерпретаторе, который я написал, когда он приблизился к 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)!
Другие вопросы по тегам