From 956b81b44fe3dfe6c641f9c94f1f46d9c10a91fc Mon Sep 17 00:00:00 2001 From: Jonathan Wright Date: Sun, 4 Nov 2007 20:10:35 +1300 Subject: [PATCH] Adding += style assignment operators - added +=, -=, *=, /= and so on to Numbers - OperatorTable addOpEqualsOperator("+=") - detect illegal operator precedence levels (<0 or >=32) - Block asString reverses += transformation - added IoMessage_rawCopySourceLocation for copying lineNumbers and labels --- libs/iovm/io/A1_OperatorTable.io | 13 +- libs/iovm/io/Block.io | 8 +- libs/iovm/io/Number.io | 11 ++ libs/iovm/source/IoMessage.c | 6 +- libs/iovm/source/IoMessage.h | 5 +- libs/iovm/source/IoMessage_opShuffle.c | 222 +++++++++++++++++++++++---------- 6 files changed, 191 insertions(+), 74 deletions(-) diff --git a/libs/iovm/io/A1_OperatorTable.io b/libs/iovm/io/A1_OperatorTable.io index 7800171..6a85fc5 100644 --- a/libs/iovm/io/A1_OperatorTable.io +++ b/libs/iovm/io/A1_OperatorTable.io @@ -1,6 +1,9 @@ OperatorTable do( addOperator := method(symbol, precedence, precedence = precedence ifNilEval(0) + if(precedence < 0 or precedence >= precedenceLevelCount, + Exception raise("Precedence for operators must be between 0 and " .. levelCount - 1 .. ". Precedence was " .. precedence .. ".") + ) operators atPut(symbol, precedence) self ) @@ -10,6 +13,10 @@ OperatorTable do( self ) + addOpEqualsOperator := method(symbol, + opEqualsOperators atPut(symbol, nil) + ) + asString := method( s := Sequence clone appendSeq(OperatorTable asSimpleString, ":\n") @@ -18,15 +25,19 @@ OperatorTable do( s appendSeq("\n ", precedence asString alignLeft(4), OperatorTable operators select(k, v, v == precedence) keys sort join(" ")) ) - s appendSeq("\n\nAssign Operators") + s appendSeq("\n\nAssignment Operators") OperatorTable assignOperators keys sort foreach(symbol, name := OperatorTable assignOperators at(symbol) s appendSeq("\n ", symbol alignLeft(4), name) ) + s appendSeq("\n\nOp-Equals Operators") + s appendSeq("\n ", OperatorTable opEqualsOperators keys sort join(" ")) + s appendSeq("\n\n") s appendSeq("To add a new operator: OperatorTable addOperator(\"+\", 4) and implement the + message.\n") s appendSeq("To add a new assign operator: OperatorTable addAssignOperator(\"=\", \"updateSlot\") and implement the updateSlot message.\n") + s appendSeq("To add a new op-equals operator: OperatorTable addOpEqualsOperator(\"+=\") and implement the += message.\n") s ) diff --git a/libs/iovm/io/Block.io b/libs/iovm/io/Block.io index bbefcc3..cfd2ecd 100644 --- a/libs/iovm/io/Block.io +++ b/libs/iovm/io/Block.io @@ -25,12 +25,14 @@ getSlot("Block") do( newSlot("operators") newSlot("reverseAssignOperators") + newSlot("opEqualsOperators") init := method( buffer = Sequence clone operators = OperatorTable operators reverseAssignOperators = OperatorTable reverseAssignOperators reverseAssignOperators atPut("setSlotWithType", ":=") + opEqualsOperators = OperatorTable opEqualsOperators ) appendSeq := method( @@ -107,7 +109,9 @@ getSlot("Block") do( ) ) - if(reverseAssignOperators hasKey(m name)) then( + if(m name == "updateSlot" and opEqualsOperators hasKey(m argAt(1) ?next name)) then( + formatMessage(m argAt(1)) + ) elseif(reverseAssignOperators hasKey(m name)) then( args := m arguments if(args first cachedResult, @@ -125,7 +129,7 @@ getSlot("Block") do( formatArguments(m) ) ) - ) elseif(operators hasKey(m name)) then( + ) elseif(operators hasKey(m name) or opEqualsOperators hasKey(m name)) then( appendSeq(m name) appendSeq(" ") diff --git a/libs/iovm/io/Number.io b/libs/iovm/io/Number.io index a2890ec..7976773 100644 --- a/libs/iovm/io/Number.io +++ b/libs/iovm/io/Number.io @@ -42,4 +42,15 @@ Number do( docSlot("minMax(low, high)", "Returns a number between or equal to low and high. If the receiver is equal to or between low and high, the reciever is returned. If the reciever is less than low, low is returned. If the receiver is greater than high, high is returned.") minMax := method(low, high, min(high) max(low)) + + setSlot("+=", getSlot("+")) + setSlot("-=", getSlot("-")) + setSlot("*=", getSlot("*")) + setSlot("/=", getSlot("/")) + setSlot("%=", getSlot("%")) + setSlot("&=", getSlot("&")) + setSlot("^=", getSlot("^")) + setSlot("|=", getSlot("|")) + setSlot("<<=", getSlot("<<")) + setSlot(">>=", getSlot(">>")) ) diff --git a/libs/iovm/source/IoMessage.c b/libs/iovm/source/IoMessage.c index f7b2cf7..0596ff5 100644 --- a/libs/iovm/source/IoMessage.c +++ b/libs/iovm/source/IoMessage.c @@ -181,13 +181,17 @@ void IoMessage_copy_(IoMessage *self, IoMessage *other) } } - if (DATA(other)->next) IOREF(DATA(other)->next); DATA(self)->next = DATA(other)->next; if (DATA(other)->cachedResult) IOREF(DATA(other)->cachedResult); DATA(self)->cachedResult = DATA(other)->cachedResult; + IoMessage_rawCopySourceLocation(self, other); +} + +void IoMessage_rawCopySourceLocation(IoMessage *self, IoMessage *other) +{ //DATA(self)->charNumber = DATA(other)->charNumber; DATA(self)->lineNumber = DATA(other)->lineNumber; diff --git a/libs/iovm/source/IoMessage.h b/libs/iovm/source/IoMessage.h index 742f5a5..d4c1f83 100644 --- a/libs/iovm/source/IoMessage.h +++ b/libs/iovm/source/IoMessage.h @@ -67,11 +67,13 @@ IOVM_API IoMessage *IoMessage_newWithName_andCachedArg_(void *state, IoSymbol *s IOVM_API void IoMessage_mark(IoMessage *self); IOVM_API void IoMessage_free(IoMessage *self); -IOVM_API void IoMessage_label_(IoMessage *self, IoSymbol *ioSymbol); // sets label for children too +IOVM_API IoSymbol *IoMessage_rawLabel(IoMessage *self); IOVM_API int IoMessage_rawLineNumber(IoMessage *self); IOVM_API int IoMessage_rawCharNumber(IoMessage *self); +IOVM_API void IoMessage_label_(IoMessage *self, IoSymbol *ioSymbol); // sets label for children too IOVM_API void IoMessage_rawSetLineNumber_(IoMessage *self, int n); IOVM_API void IoMessage_rawSetCharNumber_(IoMessage *self, int n); +IOVM_API void IoMessage_rawCopySourceLocation(IoMessage *self, IoMessage *other); IOVM_API List *IoMessage_rawArgList(IoMessage *self); IOVM_API unsigned char IoMessage_needsEvaluation(IoMessage *self); @@ -202,7 +204,6 @@ IOVM_API IoMessage *IoMessage_asMessageWithEvaluatedArgs(IoMessage *self, IoObje IOVM_API void IoMessage_addArg_(IoMessage *self, IoMessage *m); IOVM_API IoMessage *IoMessage_rawArgAt_(IoMessage *self, int n); -IOVM_API IoSymbol *IoMessage_rawLabel(IoMessage *self); IOVM_API List *IoMessage_rawArgs(IoMessage *self); IOVM_API UArray *IoMessage_asMinimalStackEntryDescription(IoMessage *self); diff --git a/libs/iovm/source/IoMessage_opShuffle.c b/libs/iovm/source/IoMessage_opShuffle.c index f269c56..f22222f 100644 --- a/libs/iovm/source/IoMessage_opShuffle.c +++ b/libs/iovm/source/IoMessage_opShuffle.c @@ -9,6 +9,8 @@ #define DATA(self) ((IoMessageData *)IoObject_dataPointer(self)) +#define IO_OP_MAX_LEVEL 32 + IoMap *IoState_createOperatorTable(IoState *state) { typedef struct OpTable { @@ -74,36 +76,28 @@ IoMap *IoState_createOperatorTable(IoState *state) IoMap *IoState_createAssignOperatorTable(IoState *state) { - typedef struct { - char *symbol; - char *name; - } OpTable; + IoMap *self = IoMap_new(state); - OpTable ops[] = { - {":=", "setSlot"}, - {"=", "updateSlot"}, - {"::=", "newSlot"}, + IoMap_rawAtPut(self, IOSYMBOL(":="), IOSYMBOL("setSlot")); + IoMap_rawAtPut(self, IOSYMBOL("="), IOSYMBOL("updateSlot")); + IoMap_rawAtPut(self, IOSYMBOL("::="), IOSYMBOL("newSlot")); - {NULL, 0}, - }; + return self; +} +IoMap *IoState_createOpEqualsOperatorTable(IoState *state) +{ IoMap *self = IoMap_new(state); - OpTable *op = ops; - while (op->symbol) - { - IoMap_rawAtPut(self, IOSYMBOL(op->symbol), IOSYMBOL(op->name)); - op ++; - } + char* operators[] = {"+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=", "<<=", ">>=", NULL}; + char **op; + + for (op = operators; *op; op++) + IoMap_rawAtPut(self, IOSYMBOL(*op), IONIL(self)); return self; } - -// 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 { @@ -123,6 +117,7 @@ typedef struct { List *stack; IoMap *operatorTable; IoMap *assignOperatorTable; + IoMap *opEqualsOperatorTable; } Levels; void Levels_reset(Levels *self) @@ -149,6 +144,27 @@ void Levels_reset(Levels *self) // --- Levels ---------------------------------------------------------- +IoMap *getOpTable(IoObject *self, const char *slotName, IoMap *create(IoState *state)) +{ + IoSymbol *symbol = IoState_symbolWithCString_(IOSTATE, slotName); + IoObject *operators = IoObject_rawGetSlot_(self, symbol); + + if (operators && ISMAP(operators)) + { + return operators; + } + else + { + // Not strictly correct as if the message has it's own empty + // OperatorTable slot, we'll create one for it instead of using + // Core Message OperatorTable operators. Oh well. + + IoMap *result = create(IOSTATE); + IoObject_setSlot_to_(self, symbol, result); + return result; + } +} + Levels *Levels_new(IoMessage *msg) { Levels *self = io_calloc(1, sizeof(Levels)); @@ -156,7 +172,7 @@ Levels *Levels_new(IoMessage *msg) IoState *state = IoObject_state(msg); IoSymbol *operatorTableSymbol = IoState_symbolWithCString_(state, "OperatorTable"); - // Lets be ultra flexable, and try to use the first message's operator table. + // Be ultra flexable, and try to use the first message's operator table. IoObject *opTable = IoObject_rawGetSlot_(msg, operatorTableSymbol); // Otherwise, use Core OperatorTable, and if that doesn't exist, create it. @@ -167,51 +183,18 @@ Levels *Levels_new(IoMessage *msg) // Message's OperatorTable opTable = IoObject_rawGetSlot_(state->core, operatorTableSymbol); - // If Core doesn't have an OperatorTable, lets create it. + // If Core doesn't have an OperatorTable, then create it. if (opTable == NULL) { opTable = IoObject_new(state); IoObject_setSlot_to_(state->core, operatorTableSymbol, opTable); + IoObject_setSlot_to_(opTable, IoState_symbolWithCString_(state, "precedenceLevelCount"), IoState_numberWithDouble_(state, IO_OP_MAX_LEVEL)); } } - { - IoSymbol *operatorsSymbol = IoState_symbolWithCString_(state, "operators"); - IoObject *operators = IoObject_rawGetSlot_(opTable, operatorsSymbol); - - if (operators && ISMAP(operators)) - { - self->operatorTable = operators; - } - else - { - // Not strictly correct as if the message has it's own empty - // OperatorTable slot, we'll create one for it instead of using - // Core Message OperatorTable operators. Oh well. - - self->operatorTable = IoState_createOperatorTable(state); - IoObject_setSlot_to_(opTable, operatorsSymbol, self->operatorTable); - } - } - - { - IoSymbol *assignOperatorsSymbol = IoState_symbolWithCString_(state, "assignOperators"); - IoObject *assignOperators = IoObject_rawGetSlot_(opTable, assignOperatorsSymbol); - - if (assignOperators && ISMAP(assignOperators)) - { - self->assignOperatorTable = assignOperators; - } - else - { - // Not strictly correct as if the message has it's own empty - // OperatorTable slot, we'll create one for it instead of using - // Core Message OperatorTable operators. Oh well. - - self->assignOperatorTable = IoState_createAssignOperatorTable(state); - IoObject_setSlot_to_(opTable, assignOperatorsSymbol, self->assignOperatorTable); - } - } + self->operatorTable = getOpTable(opTable, "operators", IoState_createOperatorTable); + self->assignOperatorTable = getOpTable(opTable, "assignOperators", IoState_createAssignOperatorTable); + self->opEqualsOperatorTable = getOpTable(opTable, "opEqualsOperators", IoState_createOpEqualsOperatorTable); self->stack = List_new(); Levels_reset(self); @@ -220,7 +203,7 @@ Levels *Levels_new(IoMessage *msg) void Levels_free(Levels *self) { - List_free(self->stack); + List_free(self->stack); io_free(self); } @@ -335,12 +318,18 @@ int Levels_levelForOp(Levels *self, char *messageName, IoSymbol *messageSymbol, if (ISNUMBER(value)) { - return IoNumber_asInt((IoNumber*)value); + int precedence = IoNumber_asInt((IoNumber*)value); + if (precedence < 0 || precedence >= IO_OP_MAX_LEVEL) + { + IoState_error_(IoObject_state(msg), msg, "compile error: Precedence for operators must be between 0 and %d. Precedence was %d.", IO_OP_MAX_LEVEL - 1, precedence); + } + + return precedence; } else { IoState_error_(IoObject_state(msg), msg, "compile error: Value for '%s' in Message OperatorTable operators is not a number. Values in the OperatorTable operators are numbers which indicate the precedence of the operator.", messageName); - return -1; // To keep the compiler happy. + return -1; // The C compiler doesn't know that IoState_error_() will never return. } } @@ -349,6 +338,11 @@ int Levels_isAssignOperator(Levels *self, IoSymbol *operator) return IoMap_rawAt(self->assignOperatorTable, operator) != NULL; } +int Levels_isOpEqualsOperator(Levels *self, IoSymbol *operator) +{ + return IoMap_rawAt(self->opEqualsOperatorTable, operator) != NULL; +} + IoSymbol *Levels_nameForAssignOperator(Levels *self, IoState *state, IoSymbol *operator, IoSymbol *slotName, IoMessage *msg) { IoObject *value = IoMap_rawAt(self->assignOperatorTable, operator); @@ -407,6 +401,11 @@ void Levels_attach(Levels *self, IoMessage *msg, List *expressions) IoState_error_(state, msg, "compile error: The symbol to the left of %s cannot have arguments.", messageName); } + if (msgArgCount > 1) // `setSlot("a") :=(b, c, d) e ;` + { + IoState_error_(state, msg, "compile error: Assign operator passed multiple arguments, e.g., a := (b, c).", messageName); + } + { // `a := b ;` @@ -414,6 +413,8 @@ void Levels_attach(Levels *self, IoMessage *msg, List *expressions) IoSymbol *quotedSlotName = IoSeq_newSymbolWithFormat_(state, "\"%s\"", CSTRING(slotName)); IoMessage *slotNameMessage = IoMessage_newWithName_returnsValue_(state, quotedSlotName, slotName); + IoMessage_rawCopySourceLocation(slotNameMessage, attaching); + // `a := b ;` -> `a("a") := b ;` IoMessage_addArg_(attaching, slotNameMessage); @@ -425,11 +426,6 @@ void Levels_attach(Levels *self, IoMessage *msg, List *expressions) currentLevel->type = ATTACH; - if (msgArgCount > 1) // `setSlot("a") :=(b, c, d) e ;` - { - IoState_error_(state, msg, "compile error: Assign operator passed multiple arguments, e.g., a := (b, c).", messageName); - } - if (msgArgCount > 0) // `setSlot("a") :=(b c) d e ;` { // `b c` @@ -444,6 +440,8 @@ void Levels_attach(Levels *self, IoMessage *msg, List *expressions) // `()` IoMessage *foo = IoMessage_newWithName_(state, IoState_symbolWithCString_(state, "")); + IoMessage_rawCopySourceLocation(foo, attaching); + // `()` -> `(b c)` IoMessage_addArg_(foo, arg); @@ -498,6 +496,92 @@ void Levels_attach(Levels *self, IoMessage *msg, List *expressions) // make sure b in `1 := b` gets executed IoMessage_cachedResult_(attaching, NULL); } + else if (Levels_isOpEqualsOperator(self, messageSymbol)) + { + Level *currentLevel = Levels_currentLevel(self); + IoMessage *attaching = currentLevel->message; + + 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); + } + + if (msgArgCount > 1) // `setSlot("a") :=(b, c, d) e ;` + { + IoState_error_(state, msg, "compile error: Assign operator passed multiple arguments, e.g., a := (b, c).", messageName); + } + + currentLevel->type = ATTACH; + + { + IoMessage *newChain = IoMessage_rawClone(attaching); + IoMessage *newOp = IoMessage_rawClone(msg); + IoMessage_rawSetNext(newChain, newOp); + + IoMessage_rawCopySourceLocation(newChain, attaching); + IoMessage_rawCopySourceLocation(newOp, attaching); + + if (msgArgCount > 0) + { + IoMessage *brackets = IoMessage_newWithName_(state, IoState_symbolWithCString_(state, "")); + + IoMessage_rawCopySourceLocation(brackets, attaching); + + List_copy_(IoMessage_rawArgList(brackets), IoMessage_rawArgList(msg)); + List_removeAll(IoMessage_rawArgList(msg)); + + IoMessage_rawSetNext(brackets, DATA(msg)->next); + IoMessage_addArg_(newOp, brackets); + } + else + { + IoMessage_addArg_(newOp, DATA(msg)->next); + } + + { + IoSymbol *slotName = DATA(attaching)->name; + IoSymbol *quotedSlotName = IoSeq_newSymbolWithFormat_(state, "\"%s\"", CSTRING(slotName)); + IoMessage *slotNameMessage = IoMessage_newWithName_returnsValue_(state, quotedSlotName, slotName); + + IoMessage_rawCopySourceLocation(slotNameMessage, attaching); + + IoMessage_addArg_(attaching, slotNameMessage); + IoMessage_addArg_(attaching, newChain); + } + } + + DATA(attaching)->name = IoObject_addingRef_(attaching, state->updateSlotSymbol); + + if (DATA(msg)->next != NULL && !IoMessage_rawIsEOL(DATA(msg)->next)) + { + List_push_(expressions, DATA(msg)->next); + } + + { + IoMessage *last = msg; + while (DATA(last)->next != NULL && !IoMessage_rawIsEOL(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); + } + } + } else if (IoMessage_rawIsEOL(msg)) { Levels_popDownTo(self, IO_OP_MAX_LEVEL-1); @@ -509,6 +593,8 @@ void Levels_attach(Levels *self, IoMessage *msg, List *expressions) { // move arguments off to their own message to make () after operators behave like C's grouping () IoMessage *brackets = IoMessage_newWithName_(state, IoState_symbolWithCString_(state, "")); + + IoMessage_rawCopySourceLocation(brackets, msg); List_copy_(IoMessage_rawArgList(brackets), IoMessage_rawArgList(msg)); List_removeAll(IoMessage_rawArgList(msg)); -- 2.11.4.GIT