Writing a schema interpreter using FPC: distribution and pointers

As a historian, writing the Scheme interpreter in FPC already at the first stage will be a serious task for me. :) I follow Peter Michaux's blog, which showed how to do it in C (there is also a translation to Ada , which can be useful for Pascal).

Consider these two functions from C by Michaud's work (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; } 

As far as I understand (just basic reading knowledge in C), the make_fixnum constructor returns a pointer to a structure (tagged data of type fixnum); for the constructed object, memory should be allocated (thanks to @David Heffernan for his item yesterday).

This is my translation to FPC so far, which compiles without errors:

 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. 

But...

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

I suspect there is a serious flaw in the way I use and reference pointers.

Many thanks to everyone who helps me understand how this pointer and memory work (links to frequently asked questions, documents, etc.).

+4
source share
1 answer

The AllocateObject function is invalid. It creates a new object in the variable x, but does not pass the created object to the calling function, since it is called by the value. If you change the calling convention, it works:

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

You can see if you look at the fixnum variable in the debugger, it remains zero.



Not related to your question, I don't think it is a good idea to use records in the interpreter. It soon turns into a memory controlling a nightmare (at least this happened in the interpreter, which I wrote when it approached 20 kloc, and I had to replace the entries as follows :)

Instead of your entry

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

you can use classes, for example:

 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; 

Then you easily create it with

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

If there are no circular links in the schema implementation, you can even use interfaces. This link is then counted and automatically released:

 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)! 
+6
source

All Articles