home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
RiscOS
/
APP
/
DEVS
/
LISP
/
FOOLS.ZIP
/
Fools
/
doc
/
datatypes.
next >
Wrap
Text File
|
1991-10-31
|
4KB
|
143 lines
extending data types
One way to add a new data type to fools' lisp is by extending
existing data types -- for example, extending the number class
to include characters.
The print representation of a character is #\* where * is a letter,
digit, or punctuation mark. #\space, #\newline, and #\tab represent
the space, newline, and tab characters.
Often a data type will have creator and accessor routines (for example
delay, force, make-environment, the-environment). Parsing a new
datatype's print representation may not be worthwhile.
The type of an object is stored as a struct. Each new operation
of the class has a field in the struct for the dispatch function.
objDestroy, for example, calls all the destroy functions for an object
from class to superclass order). The first entry in the struct is
the struct of the defining class (the superclass). In this case
numberClass_t is the inherited class for characters.
typedef struct charClass_s {
numberClass_t inherit;
F_INT code;
} charClass_t, *charClass;
#define DEFCHAR(number, code) { number, code }
The actual object is a pointer to an instance struct which holds data
specific to a character instance. Fully encapsulating the number
instance is unnecessary so the basic instance is inherited instead.
typedef struct charInst_s {
basicInst_t inherit;
char code;
} charInst_t, *charInst;
Character is the class variable (a constant value used for type checking).
extern charClass_t protoChar;
#define Character ((Class)&protoChar)
The class struct is initialized at compile time. See Basic.h and
Number.h for the layouts of the basic and number class and instance
structs. The destroy slot is filled with (F_VOID)NULL which means
that a character instance does not need to free anything not already
freed by it's superclass.
charClass_t protoChar =
DEFCHAR(DEFNUMBER(DEFBASIC(&protoNumber, charInst_t, charPrint,
(F_VOID)NULL, charType),
charNumSet, charNum),
_charCode);
The functions objNumSet and objNum dispatch on the type of the object,
so charNumSet and charNum must fit into the type restriction of objNumSet
and objNum.
Whether charNumSet should check the range of val is up to you.
void charNumSet(ch, val)
Obj ch;
double val;
{
int c;
ASSERT(objIsClass(ch, Character));
c = (int)val;
if (c < 0 || c > 255 || c != (int)floor(val))
errorPrint(BadVal, "%O is not a valid character code", ch);
DATA(ch, code, charInst) = (char)c;
}
double charNum(ch)
Obj ch;
{
ASSERT(objIsClass(ch, Character));
return (double)DATA(ch, code, charInst);
}
The printing routine is a basic function that also needs to be replaced.
static void charPrint(ch, f)
Obj ch;
FILE *f;
{
char c;
c = DATA(ch, code, charInst);
if (isprint(c))
(void)fprintf(f, "#\\%c", c);
else switch(c) {
case '\n':
(void)fputs("#\\newline", f);
break ;
case '\t':
(void)fputs("#\\tab", f);
break ;
default:
(void)fprintf(f, "#\\%.3o", c);
break ;
}
}
The type function must be defined for all classes and returns a symbol
corresponding to the type of the object. objIntern returns the unique
symbol representing its string argument.
static Obj charType()
{
return objIntern("char");
}
charCode is a generic dispatching routine.
int charCode(ch)
Obj ch;
{
ASSERT(objIsClass(ch, Character));
return PROC(ch, code, charClass)(ch);
}
This is the function called by charCode for Character objects:
static int _charCode(ch)
Obj ch;
{
return DATA(ch, code, charInst);
}
A constructor is also helpful.
Obj newChar(alloc, ch)
F_OBJ alloc;
char ch;
{
Obj new;
new = (*alloc)(Character);
DATA(new, code, charInst) = ch;
return new;
}