/*
docCopyright("Jonathan Wright; Steve Dekorte (2002)", 2006)
docLicense("BSD revised")
*/
#include "IoMessage_opShuffle.h"
#include "IoMap.h"
#include "IoNumber.h"
#define DATA(self) ((IoMessageData *)IoObject_dataPointer(self))
typedef struct IoOpTable {
char *opString;
int level;
} IoOpTable;
int IoToken_LevelForOp_(char *s)
{
IoOpTable ops[] = {
{"@", 0},
{"@@", 0},
{"'", 0},
{".", 0},
{"?", 0},
{"(", 0},
{")", 0},
{"**", 1},
{"++", 2},
{"--", 2},
{"*", 3},
{"/", 3},
{"%", 3},
{"+", 4},
{"-", 4},
{"<<", 5},
{">>", 5},
{">", 6},
{"<", 6},
{"<=", 6},
{">=", 6},
{"==", 7},
{"!=", 7},
{"&", 8},
{"^", 9},
{"|", 10},
{"and", 11},
{"&&", 11},
{"or", 12},
{"||", 12},
{"..", 13},
{"=", 14},
{"+=", 14},
{"-=", 14},
{"*=", 14},
{"/=", 14},
{"%=", 14},
{"&=", 14},
{"^=", 14},
{"|=", 14},
{"<<=", 14},
{">>=", 14},
{":=", 14},
{"<-", 14},
{"<->", 14},
{"->", 14},
{"return", 15},
{",", 16},
{NULL, 0},
};
IoOpTable *entry = ops;
while (entry->opString)
{
if (strcmp(entry->opString, s) == 0)
{
return entry->level;
}
entry ++;
}
return -1;
}
// TODO move the IO_OP_MAX_LEVEL define to be adjacent to the op table.
// TODO Set IO_OP_MAX_LEVEL to max-1 or something massive otherwise people playing with custom operators don't have many levels available
#define IO_OP_MAX_LEVEL 17
enum LevelType {ATTACH, ARG, NEW, UNUSED};
typedef struct {
IoMessage *message;
enum LevelType type;
int precedence;
} Level;
void Level_finish(Level *self);
void Level_attachAndReplace(Level *self, IoMessage *msg);
void Level_setAwaitingFirstArg(Level *self, IoMessage *msg, int precedence);
typedef struct {
Level pool[IO_OP_MAX_LEVEL];
int currentLevel;
List *stack;
IoMap *operatorTable;
} Levels;
void Levels_reset(Levels *self)
{
int i;
self->currentLevel = 1;
for (i = 0; i < IO_OP_MAX_LEVEL; i ++)
{
Level *level = &self->pool[i];
level->type = UNUSED;
}
{
Level *level = &self->pool[0];
level->message = NULL;
level->type = NEW;
level->precedence = IO_OP_MAX_LEVEL;
}
List_removeAll(self->stack);
List_append_(self->stack, &self->pool[0]);
}
// --- Levels ----------------------------------------------------------
Levels *Levels_new(IoMessage *msg)
{
Levels *self = calloc(1, sizeof(Levels));
IoObject *opTable = IoObject_rawGetSlot_(msg, IoState_symbolWithCString_(msg->state, "OperatorTable"));
self->operatorTable = NULL;
if (opTable)
{
IoObject *rightOperators = IoObject_rawGetSlot_(opTable, IoState_symbolWithCString_(msg->state, "rightOperators"));
if (rightOperators)
{
if (ISMAP(rightOperators))
{
self->operatorTable = rightOperators;
}
else
{
IoState_error_(msg->state, NULL, "compile error: Message OperatorTable rightOperators is not a Map. %p %s", rightOperators, CSTRING(IoMessage_name(msg)));
}
}
}
self->stack = List_new();
Levels_reset(self);
return self;
}
void Levels_free(Levels *self)
{
List_free(self->stack);
free(self);
}
inline Level *Levels_currentLevel(Levels *self)
{
return List_top(self->stack);
}
void Levels_popDownTo(Levels *self, int targetLevel)
{
Level *level;
while (level = List_top(self->stack), level->precedence <= targetLevel && level->type != ARG)
{
Level_finish(List_pop(self->stack));
self->currentLevel--;
}
}
void Levels_attachToTopAndPush(Levels *self, IoMessage *msg, int precedence)
{
Level *level = NULL;
{
Level *top = List_top(self->stack);
Level_attachAndReplace(top, msg);
}
{
// TODO: Check for overflow of the pool.
if (self->currentLevel >= IO_OP_MAX_LEVEL)
{
IoState_error_(msg->state, NULL, "compile error: Overflowed operator stack. Only %d levels of operators currently supported.", IO_OP_MAX_LEVEL-1);
}
level = &self->pool[self->currentLevel++];
Level_setAwaitingFirstArg(level, msg, precedence);
List_append_(self->stack, level);
}
}
void Level_finish(Level *self)
{
if (self->message)
{
IoMessage_rawSetNext(self->message, NULL);
}
self->type = UNUSED;
}
void Level_attach(Level *self, IoMessage *msg)
{
switch (self->type)
{
case ATTACH:
IoMessage_rawSetNext(self->message, msg);
break;
case ARG:
IoMessage_addArg_(self->message, msg);
break;
case NEW:
self->message = msg;
break;
case UNUSED:
break;
}
}
void Level_attachAndReplace(Level *self, IoMessage *msg)
{
Level_attach(self, msg);
self->type = ATTACH;
self->message = msg;
}
void Level_setAwaitingFirstArg(Level *self, IoMessage *msg, int precedence)
{
self->type = ARG;
self->message = msg;
self->precedence = precedence;
}
void Level_setAlreadyHasArgs(Level *self, IoMessage *msg)
{
self->type = ATTACH;
self->message = msg;
}
int Levels_levelForOp(Levels *self, char *messageName, IoSymbol *messageSymbol, IoMessage *msg)
{
if (self->operatorTable)
{
IoObject *value = IoMap_rawAt(self->operatorTable, messageSymbol);
if (!value)
{
return -1;
}
if (ISNUMBER(value))
{
return IoNumber_asInt((IoNumber*)value);
}
else
{
IoState_error_(msg->state, msg, "compile error: Value for '%s' in Message OperatorTable is not a number. Values in the OperatorTable are numbers which indicate the precedence of the operator.", messageName);
return -1; // To keep the compiler happy.
}
}
else
{
return IoToken_LevelForOp_(messageName);
}
}
void Levels_attach(Levels *self, IoMessage *msg, List *expressions)
{
// TODO clean up this method.
IoSymbol *messageSymbol = IoMessage_name(msg);
char *messageName = CSTRING(messageSymbol);
int precedence = Levels_levelForOp(self, messageName, messageSymbol, msg);
// `o a := b c ; d` becomes `o setSlot("a", b c) ; d`
//
// a attaching
// := msg
// b c msg->next
if (strcmp(messageName, ":=") == 0 || strcmp(messageName, "=") == 0)
{
IoState *state = (IoState *)(msg->state);
Level *currentLevel = Levels_currentLevel(self);
IoMessage *attaching = currentLevel->message;
IoSymbol *setSlotName;
if (attaching == NULL) // `:= b ;`
{
// Could be handled as, message(:= 42) -> setSlot(nil, 42)
IoState_error_(state, msg, "compile error: %s requires a symbol to its left.", messageName);
}
if (IoMessage_argCount(attaching) > 0) // `a(1,2,3) := b ;`
{
IoState_error_(state, msg, "compile error: The symbol to the left of %s cannot have arguments.", messageName);
}
{
// `a := b ;`
IoSymbol *slotName = DATA(attaching)->name;
IoSymbol *quotedSlotName = IoSeq_newSymbolWithFormat_(state, "\"%s\"", CSTRING(slotName));
IoMessage *slotNameMessage = IoMessage_newWithName_returnsValue_(state, quotedSlotName, slotName);
// `a := b ;` -> `a("a") := b ;`
IoMessage_addArg_(attaching, slotNameMessage);
if (strcmp(messageName, ":=") == 0)
{
if (isupper(CSTRING(slotName)[0]))
{
setSlotName = state->setSlotWithTypeSymbol;
}
else
{
setSlotName = state->setSlotSymbol;
}
}
else
{
setSlotName = state->updateSlotSymbol;
}
}
// `a("a") := b ;` -> `setSlot("a") := b ;`
DATA(attaching)->name = IoObject_addingRef_(attaching, setSlotName);
currentLevel->type = ATTACH;
if (IoMessage_argCount(msg) > 0) // `setSlot("a") :=(b c) d e ;`
{
// `b c`
IoMessage *arg = IoMessage_rawArgAt_(msg, 0);
if (DATA(msg)->next == NULL || IoMessage_isEOL(DATA(msg)->next))
{
IoMessage_addArg_(attaching, arg);
}
else
{
// `()`
IoMessage *foo = IoMessage_newWithName_(state, IoState_symbolWithCString_(state, ""));
// `()` -> `(b c)`
IoMessage_addArg_(foo, arg);
// `(b c)` -> `(b c) d e ;`
IoMessage_rawSetNext(foo, DATA(msg)->next);
// `setSlot("a") :=(b c) d e ;` -> `setSlot("a", (b c) d e ;) :=(b c) d e ;`
IoMessage_addArg_(attaching, foo);
}
}
else // `setSlot("a") := b ;`
{
// `setSlot("a") :=` or `setSlot("a") := ;`
IoMessage *mn = DATA(msg)->next;
IoSymbol *name = mn ? DATA(mn)->name : NULL;
IoSymbol *semi = ((IoState *)(msg->tag->state))->semicolonSymbol;
//if (mn == NULL || IoMessage_isEOL(mn))
if (mn == NULL || name == semi)
{
IoState_error_(state, msg, "compile error: %s must be followed by a value.", messageName);
}
// `setSlot("a") := b c ;` -> `setSlot("a", b c ;) := b c ;`
IoMessage_addArg_(attaching, DATA(msg)->next);
}
// process the value (`b c d`) later (`setSlot("a", b c d) := b c d ;`)
if (DATA(msg)->next != NULL && !IoMessage_isEOL(DATA(msg)->next))
{
List_push_(expressions, DATA(msg)->next);
}
{
IoMessage *last = msg;
while (DATA(last)->next != NULL && !IoMessage_isEOL(DATA(last)->next))
{
last = DATA(last)->next;
}
IoMessage_rawSetNext(attaching, DATA(last)->next);
// Continue processing in IoMessage_opShuffle loop
IoMessage_rawSetNext(msg, DATA(last)->next);
if (last != msg)
{
IoMessage_rawSetNext(last, NULL);
}
}
// make sure b in `1 := b` gets executed
IoMessage_cachedResult_(attaching, NULL);
}
else if (IoMessage_isEOL(msg))
{
Levels_popDownTo(self, IO_OP_MAX_LEVEL-1);
Level_attachAndReplace(Levels_currentLevel(self), msg);
}
else if (precedence != -1)
{
if (IoMessage_argCount(msg) > 0)
{
Level_setAlreadyHasArgs(Levels_currentLevel(self), msg);
}
else
{
Levels_popDownTo(self, precedence);
Levels_attachToTopAndPush(self, msg, precedence);
}
}
else
{
Level_attachAndReplace(Levels_currentLevel(self), msg);
}
}
void Levels_nextMessage(Levels *self)
{
Level *level;
while ((level = List_pop(self->stack)))
{
Level_finish(level);
}
Levels_reset(self);
}
void IoMessage_opShuffle_(IoMessage *self)
{
if (IoObject_rawGetSlot_(self, IOSTATE->opShuffleSymbol) && IoMessage_name(self) != IOSTATE->noShufflingSymbol)
{
IoMessage_locals_performOn_(IOSTATE->opShuffleMessage, IOSTATE->lobby, self);
}
}
IoMessage *IoMessage_opShuffle(IoMessage *self, IoObject *locals, IoMessage *m)
{
Levels *levels = Levels_new(self);
List *expressions = List_new();
List_push_(expressions, self);
while (List_size(expressions) >= 1)
{
IoMessage *n = List_pop(expressions);
do
{
Levels_attach(levels, n, expressions);
List_appendSeq_(expressions, DATA(n)->args);
} while (n = DATA(n)->next);
Levels_nextMessage(levels);
}
List_free(expressions);
Levels_free(levels);
return self;
}
syntax highlighted by Code2HTML, v. 0.9.1