#include <cstdio>
#include <cctype>
#include <cstdlib>
#include "consts.h"
#include "builtin.h"
#include "model.h"
#include "exc.h"
#include "types.h"
using std::stringstream;
extern EmptyList *empty_list;
#define ARGS_EXACTLY_TWO \
if (args == empty_list || \
args->cdr == empty_list || \
TO_PAIR(args->cdr)->cdr != empty_list) \
throw TokenError(name, RUN_ERR_WRONG_NUM_OF_ARGS)
#define ARGS_EXACTLY_ONE \
if (args == empty_list || \
args->cdr != empty_list ) \
throw TokenError(name, RUN_ERR_WRONG_NUM_OF_ARGS)
#define ARGS_AT_LEAST_ONE \
if (args == empty_list) \
throw TokenError(name, RUN_ERR_WRONG_NUM_OF_ARGS)
SpecialOptIf::SpecialOptIf() : SpecialOptObj("if") {}
void SpecialOptIf::prepare(Pair *pc) {
#define IF_EXP_ERR \
throw TokenError(name, RUN_ERR_WRONG_NUM_OF_ARGS)
state = 0; // Prepared
Pair *first, *second, *third;
if (pc->cdr->is_pair_obj())
first = TO_PAIR(pc->cdr);
else
IF_EXP_ERR;
if (first->cdr->is_pair_obj())
second = TO_PAIR(first->cdr);
else
IF_EXP_ERR;
if (second->cdr != empty_list)
{
if (second->cdr->is_pair_obj())
{
third = TO_PAIR(second->cdr);
if (third->cdr != empty_list)
IF_EXP_ERR;
}
else
IF_EXP_ERR;
}
pc->next = first;
first->next = NULL; // skip <consequence> and <alternative>
}
void SpecialOptIf::pre_call(Pair *args, Pair *pc,
Environment *envt) {
// prepare has guaranteed ...
pc = TO_PAIR(pc->car);
Pair *first = TO_PAIR(pc->cdr);
Pair *second = TO_PAIR(first->cdr);
Pair *third = TO_PAIR(second->cdr);
// Condition evaluated and the decision is made
state = 1;
if (TO_PAIR(args->cdr)->car->is_true())
{
pc->next = second;
second->next = NULL;
}
else
{
pc->next = third;
third->next = NULL;
}
}
EvalObj *SpecialOptIf::post_call(Pair *args, Pair *pc,
Environment *envt) {
// Value already evaluated, so just return it
return TO_PAIR(args->cdr)->car;
}
Pair *SpecialOptIf::call(Pair *args, Environment * &envt,
Continuation * &cont, FrameObj ** &top_ptr) {
Pair *ret_addr = static_cast<RetAddr*>(*top_ptr)->addr;
if (state)
{
*top_ptr++ = post_call(args, ret_addr, envt);
return ret_addr->next; // Move to the next instruction
}
else
{
pre_call(