Я пишу простой интерпретатор Лиспа, и у меня определены следующие struct
и enum
:
typedef enum {
STRING,
INTEGER,
FLOAT,
FUNCTION,
VARIABLE,
SYMBOL,
NIL
} atom_e;
typedef union {
char* string;
int integer;
float decimal;
} data_t;
typedef struct {
data_t data;
atom_e type;
void* next;
} atom_t;
typedef struct {
void* head;
} list_t;
atom_e
относится к поддерживаемым типам атомов для моего Лиспа.
data_t
используется для хранения каждого атома. Он используется только в atom_t
.
list_t
используется для сбора atom_t
s. Он имеет head
, который указывает либо на
atom_t
или list_t
(в случае вложенных списков)
atom_t
— это структура атома. Он состоит из атома (хранится в data
)
, описание его типа (type
) и void*
. Этот пустой указатель может указывать на другой atom_t
или list_t
.
Я разработал его таким образом, чтобы при написании Лиспа было более понятно, когда list является вложенным и каков его родительский/дочерний список. Разбор s-exp всегда даст вы используете list_t*, потому что весь допустимый код Lisp начинается с открывающей скобки, знака начала списка.
Сейчас я нахожусь на стадии "eval" Лиспа, и функция eval
работает следующим образом:
Если next
(в atom_t
или list_t
) указывает на atom_t
, eval
этот атом и следующие за ним (т.е. (set x 10)
).
Если next
указывает на list_t
, оцените весь этот список, атом за атомом. (т.е. "(set x (* 5 2))")
Я разработал его, как описано выше, предполагая, что C предоставляет встроенную функцию type()
или isinstance()
, которую я мог бы использовать в функции eval
— насколько мне известно, это не так. Как бы я имитировал функцию Python isinstance()
в C, чтобы я мог сравнивать типы указателей void? Я хотел бы придерживаться ANSI C, где это возможно, последней версии C99, чтобы обеспечить максимальную переносимость.