summaryrefslogtreecommitdiff
path: root/jim.c
diff options
context:
space:
mode:
Diffstat (limited to 'jim.c')
-rw-r--r--jim.c2384
1 files changed, 1174 insertions, 1210 deletions
diff --git a/jim.c b/jim.c
index bb32f09..499fa69 100644
--- a/jim.c
+++ b/jim.c
@@ -41,7 +41,9 @@
* official policies, either expressed or implied, of the Jim Tcl Project.
**/
#define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* Mostly just for environ */
+#endif
#include <stdio.h>
#include <stdlib.h>
@@ -222,7 +224,7 @@ first:
pattern += utf8_tounicode_case(pattern, &start, nocase);
if (pattern[0] == '-' && pattern[1]) {
/* skip '-' */
- pattern += utf8_tounicode(pattern, &pchar);
+ pattern++;
pattern += utf8_tounicode_case(pattern, &end, nocase);
/* Handle reversed range too */
@@ -366,9 +368,12 @@ static int JimStringCompareLen(const char *s1, const char *s2, int maxchars, int
return 0;
}
-/* Search 's1' inside 's2', starting to search from char 'index' of 's2'.
+/* Search for 's1' inside 's2', starting to search from char 'index' of 's2'.
* The index of the first occurrence of s1 in s2 is returned.
- * If s1 is not found inside s2, -1 is returned. */
+ * If s1 is not found inside s2, -1 is returned.
+ *
+ * Note: Lengths and return value are in bytes, not chars.
+ */
static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
{
int i;
@@ -393,7 +398,10 @@ static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int id
return -1;
}
-/**
+/* Search for the last occurrence 's1' inside 's2', starting to search from char 'index' of 's2'.
+ * The index of the last occurrence of s1 in s2 is returned.
+ * If s1 is not found inside s2, -1 is returned.
+ *
* Note: Lengths and return value are in bytes, not chars.
*/
static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
@@ -414,7 +422,7 @@ static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
#ifdef JIM_UTF8
/**
- * Note: Lengths and return value are in chars.
+ * Per JimStringLast but lengths and return value are in chars, not bytes.
*/
static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
{
@@ -450,7 +458,7 @@ static int JimCheckConversion(const char *str, const char *endptr)
return JIM_OK;
}
-/* Parses the front of a number to determine it's sign and base
+/* Parses the front of a number to determine its sign and base.
* Returns the index to start parsing according to the given base
*/
static int JimNumberBase(const char *str, int *base, int *sign)
@@ -719,7 +727,13 @@ unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
/* ----------------------------- API implementation ------------------------- */
-/* reset a hashtable already initialized */
+/*
+ * Reset a hashtable already initialized.
+ * The table data should already have been freed.
+ *
+ * Note that type and privdata are not initialised
+ * to allow the now-empty hashtable to be reused
+ */
static void JimResetHashTable(Jim_HashTable *ht)
{
ht->table = NULL;
@@ -754,17 +768,6 @@ int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *pr
return JIM_OK;
}
-/* Resize the table to the minimal size that contains all the elements,
- * but with the invariant of a USER/BUCKETS ration near to <= 1 */
-void Jim_ResizeHashTable(Jim_HashTable *ht)
-{
- int minimal = ht->used;
-
- if (minimal < JIM_HT_INITIAL_SIZE)
- minimal = JIM_HT_INITIAL_SIZE;
- Jim_ExpandHashTable(ht, minimal);
-}
-
/* Expand or create the hashtable */
void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
{
@@ -902,7 +905,9 @@ int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
return JIM_ERR; /* not found */
}
-/* Destroy an entire hash table and leave it ready for reuse */
+/* Remove all entries from the hash table
+ * and leave it empty for reuse
+ */
int Jim_FreeHashTable(Jim_HashTable *ht)
{
unsigned int i;
@@ -981,7 +986,7 @@ Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
{
/* If the hash table is empty expand it to the intial size,
- * if the table is "full" dobule its size. */
+ * if the table is "full" double its size. */
if (ht->size == 0)
Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
if (ht->size == ht->used)
@@ -1186,12 +1191,12 @@ void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
* Results of missing quotes, braces, etc. from parsing.
*/
struct JimParseMissing {
- int ch; /* At end of parse, ' ' if complete or '{', '[', '"', '\\' , '{' if incomplete */
+ int ch; /* At end of parse, ' ' if complete or '{', '[', '"', '\\', '}' if incomplete */
int line; /* Line number starting the missing token */
};
-/* Parser context structure. The same context is used both to parse
- * Tcl scripts and lists. */
+/* Parser context structure. The same context is used to parse
+ * Tcl scripts, expressions and lists. */
struct JimParserCtx
{
const char *p; /* Pointer to the point of the program we are parsing */
@@ -1337,17 +1342,17 @@ static int JimParseEol(struct JimParserCtx *pc)
** Here are the rules for parsing:
** {braced expression}
** - Count open and closing braces
-** - Backslash escapes meaning of braces
+** - Backslash escapes meaning of braces but doesn't remove the backslash
**
** "quoted expression"
-** - First double quote at start of word terminates the expression
-** - Backslash escapes quote and bracket
+** - Unescaped double quote terminates the expression
+** - Backslash escapes next char
** - [commands brackets] are counted/nested
-** - command rules apply within [brackets], not quoting rules (i.e. quotes have their own rules)
+** - command rules apply within [brackets], not quoting rules (i.e. brackets have their own rules)
**
** [command expression]
** - Count open and closing brackets
-** - Backslash escapes quote, bracket and brace
+** - Backslash escapes next char
** - [commands brackets] are counted/nested
** - "quoted expressions" are parsed according to quoting rules
** - {braced expressions} are parsed according to brace rules
@@ -1810,8 +1815,8 @@ static int odigitval(int c)
/* Perform Tcl escape substitution of 's', storing the result
* string into 'dest'. The escaped string is guaranteed to
- * be the same length or shorted than the source string.
- * Slen is the length of the string at 's'.
+ * be the same length or shorter than the source string.
+ * slen is the length of the string at 's'.
*
* The function returns the length of the resulting string. */
static int JimEscape(char *dest, const char *s, int slen)
@@ -2002,23 +2007,19 @@ static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc
start = pc->tstart;
end = pc->tend;
- if (start > end) {
+ len = (end - start) + 1;
+ if (len < 0) {
len = 0;
- token = Jim_Alloc(1);
- token[0] = '\0';
+ }
+ token = Jim_Alloc(len + 1);
+ if (pc->tt != JIM_TT_ESC) {
+ /* No escape conversion needed? Just copy it. */
+ memcpy(token, start, len);
+ token[len] = '\0';
}
else {
- len = (end - start) + 1;
- token = Jim_Alloc(len + 1);
- if (pc->tt != JIM_TT_ESC) {
- /* No escape conversion needed? Just copy it. */
- memcpy(token, start, len);
- token[len] = '\0';
- }
- else {
- /* Else convert the escape chars. */
- len = JimEscape(token, start, len);
- }
+ /* Else convert the escape chars. */
+ len = JimEscape(token, start, len);
}
return Jim_NewStringObjNoAlloc(interp, token, len);
@@ -2158,10 +2159,10 @@ Jim_Obj *Jim_NewObj(Jim_Interp *interp)
}
/* Object is returned with refCount of 0. Every
- * kind of GC implemented should take care to don't try
- * to scan objects with refCount == 0. */
+ * kind of GC implemented should take care to avoid
+ * scanning objects with refCount == 0. */
objPtr->refCount = 0;
- /* All the other fields are left not initialized to save time.
+ /* All the other fields are left uninitialized to save time.
* The caller will probably want to set them to the right
* value anyway. */
@@ -2232,7 +2233,9 @@ Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
dupPtr->bytes = NULL;
}
else if (objPtr->length == 0) {
- /* Zero length, so don't even bother with the type-specific dup, since all zero length objects look the same */
+ /* Zero length, so don't even bother with the type-specific dup,
+ * since all zero length objects look the same
+ */
dupPtr->bytes = JimEmptyStringRep;
dupPtr->length = 0;
dupPtr->typePtr = NULL;
@@ -2275,13 +2278,12 @@ const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
return objPtr->bytes;
}
-/* Just returns the length of the object's string rep */
+/* Just returns the length (in bytes) of the object's string rep */
int Jim_Length(Jim_Obj *objPtr)
{
if (objPtr->bytes == NULL) {
/* Invalid string repr. Generate it. */
- JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
- objPtr->typePtr->updateStringProc(objPtr);
+ Jim_GetString(objPtr, NULL);
}
return objPtr->length;
}
@@ -2291,9 +2293,7 @@ const char *Jim_String(Jim_Obj *objPtr)
{
if (objPtr->bytes == NULL) {
/* Invalid string repr. Generate it. */
- JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value."));
- JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
- objPtr->typePtr->updateStringProc(objPtr);
+ Jim_GetString(objPtr, NULL);
}
return objPtr->bytes;
}
@@ -2315,19 +2315,30 @@ static const Jim_ObjType dictSubstObjType = {
JIM_TYPE_NONE,
};
-static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
-{
- Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
-}
+static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
+static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static const Jim_ObjType interpolatedObjType = {
"interpolated",
FreeInterpolatedInternalRep,
- NULL,
+ DupInterpolatedInternalRep,
NULL,
JIM_TYPE_NONE,
};
+static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
+{
+ Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
+}
+
+static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
+{
+ /* Copy the interal rep */
+ dupPtr->internalRep = srcPtr->internalRep;
+ /* Need to increment the key ref count */
+ Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
+}
+
/* -----------------------------------------------------------------------------
* String Object
* ---------------------------------------------------------------------------*/
@@ -2407,9 +2418,7 @@ Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
objPtr->bytes = JimEmptyStringRep;
}
else {
- objPtr->bytes = Jim_Alloc(len + 1);
- memcpy(objPtr->bytes, s, len);
- objPtr->bytes[len] = '\0';
+ objPtr->bytes = Jim_StrDupLen(s, len);
}
objPtr->length = len;
@@ -2732,8 +2741,6 @@ static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
int len;
const char *str;
- SetStringFromAny(interp, strObjPtr);
-
str = Jim_GetString(strObjPtr, &len);
#ifdef JIM_UTF8
@@ -2756,10 +2763,6 @@ static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
const char *str;
int len;
- if (strObjPtr->typePtr != &stringObjType) {
- SetStringFromAny(interp, strObjPtr);
- }
-
str = Jim_GetString(strObjPtr, &len);
#ifdef JIM_UTF8
@@ -2784,9 +2787,7 @@ static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
const char *str;
str = Jim_GetString(strObjPtr, &len);
- if (len == 0) {
- return strObjPtr;
- }
+
#ifdef JIM_UTF8
/* Case mapping can change the utf-8 length of the string.
* But at worst it will be by one extra byte per char
@@ -3032,7 +3033,7 @@ static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass
}
for (i = 0; i < len; i++) {
- if (!isclassfunc(str[i])) {
+ if (!isclassfunc(UCHAR(str[i]))) {
Jim_SetResultBool(interp, 0);
return JIM_OK;
}
@@ -3075,9 +3076,7 @@ int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *
return 1;
}
else {
- const char *objStr = Jim_String(objPtr);
-
- if (strcmp(str, objStr) != 0)
+ if (strcmp(str, Jim_String(objPtr)) != 0)
return 0;
if (objPtr->typePtr != &comparedStringObjType) {
@@ -3399,7 +3398,7 @@ static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len
* operator (in which case the count doesn't include
* that token).
*/
-static int JimCountWordTokens(ParseToken *t)
+static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
{
int expand = 1;
int count = 0;
@@ -3411,6 +3410,13 @@ static int JimCountWordTokens(ParseToken *t)
expand = -1;
t++;
}
+ else {
+ if (script->missing == ' ') {
+ /* This is a "extra characters after close-brace" error. Report the first error */
+ script->missing = '}';
+ script->linenr = t[1].line;
+ }
+ }
}
/* Now count non-separator words */
@@ -3497,7 +3503,7 @@ static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
i++;
}
- wordtokens = JimCountWordTokens(tokenlist->list + i);
+ wordtokens = JimCountWordTokens(script, tokenlist->list + i);
if (wordtokens == 0) {
/* None, so at end of line */
@@ -3578,6 +3584,10 @@ static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
* '\\' on scripts with a trailing backslash.
*
* If the script is complete, 1 is returned, otherwise 0.
+ *
+ * If the script has extra characters after a close brace, this still returns 1,
+ * but sets *stateCharPtr to '}'
+ * Evaluating the script will give the error "extra characters after close-brace".
*/
int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
{
@@ -3585,7 +3595,7 @@ int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateChar
if (stateCharPtr) {
*stateCharPtr = script->missing;
}
- return (script->missing == ' ');
+ return script->missing == ' ' || script->missing == '}';
}
/**
@@ -3610,6 +3620,9 @@ static int JimParseCheckMissing(Jim_Interp *interp, int ch)
case '{':
msg = "missing close-brace";
break;
+ case '}':
+ msg = "extra characters after close-brace";
+ break;
case '"':
default:
msg = "missing quote";
@@ -3777,10 +3790,6 @@ static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
*
* Keys are dynamically allocated strings, Values are Jim_Var structures.
*/
-
-/* Variables HashTable Type.
- *
- * Keys are dynamic allocated strings, Values are Jim_Var structures. */
static void JimVariablesHTValDestructor(void *interp, void *val)
{
Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
@@ -3839,6 +3848,11 @@ static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
return nsObj;
}
+/**
+ * If nameObjPtr starts with "::", returns it.
+ * Otherwise returns a new object with nameObjPtr prefixed with "::".
+ * In this case, decrements the ref count of nameObjPtr.
+ */
Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
{
Jim_Obj *resultObj;
@@ -4010,6 +4024,10 @@ static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Ob
return JIM_OK;
}
+/**
+ * If the command is a proc, sets/updates the cached namespace (nsObj)
+ * based on the command name.
+ */
static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
{
#ifdef jim_ext_namespace
@@ -4022,7 +4040,7 @@ static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const ch
Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
- /* This commands shadows a global command, so a proc epoch update is required */
+ /* This command shadows a global command, so a proc epoch update is required */
Jim_InterpIncrProcEpoch(interp);
}
}
@@ -4207,12 +4225,12 @@ static const Jim_ObjType commandObjType = {
};
/* This function returns the command structure for the command name
- * stored in objPtr. It tries to specialize the objPtr to contain
- * a cached info instead to perform the lookup into the hash table
- * every time. The information cached may not be uptodate, in such
- * a case the lookup is performed and the cache updated.
+ * stored in objPtr. It specializes the objPtr to contain
+ * cached info instead of performing the lookup into the hash table
+ * every time. The information cached may not be up-to-date, in this
+ * case the lookup is performed and the cache updated.
*
- * Respects the 'upcall' setting
+ * Respects the 'upcall' setting.
*/
Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
@@ -4263,7 +4281,7 @@ found:
#endif
cmd = Jim_GetHashEntryVal(he);
- /* Free the old internal repr and set the new one. */
+ /* Free the old internal rep and set the new one. */
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &commandObjType;
objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
@@ -4437,6 +4455,9 @@ static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_O
* All the caching should also have an 'epoch' mechanism similar
* to the one used by Tcl for procedures lookup caching. */
+/**
+ * Set the variable nameObjPtr to value valObjptr.
+ */
int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
{
int err;
@@ -4500,15 +4521,12 @@ int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objP
int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
{
- Jim_Obj *nameObjPtr, *valObjPtr;
+ Jim_Obj *valObjPtr;
int result;
- nameObjPtr = Jim_NewStringObj(interp, name, -1);
valObjPtr = Jim_NewStringObj(interp, val, -1);
- Jim_IncrRefCount(nameObjPtr);
Jim_IncrRefCount(valObjPtr);
- result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
- Jim_DecrRefCount(interp, nameObjPtr);
+ result = Jim_SetVariableStr(interp, name, valObjPtr);
Jim_DecrRefCount(interp, valObjPtr);
return result;
}
@@ -4684,8 +4702,9 @@ Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flag
}
/* Unset a variable.
- * Note: On success unset invalidates all the variable objects created
- * in the current call frame incrementing. */
+ * Note: On success unset invalidates all the (cached) variable objects
+ * by incrementing callFrameEpoch
+ */
int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
{
Jim_Var *varPtr;
@@ -4849,14 +4868,13 @@ void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
}
-void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
+static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
- JIM_NOTUSED(interp);
-
- dupPtr->internalRep.dictSubstValue.varNameObjPtr =
- srcPtr->internalRep.dictSubstValue.varNameObjPtr;
- dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr;
- dupPtr->typePtr = &dictSubstObjType;
+ /* Copy the internal rep */
+ dupPtr->internalRep = srcPtr->internalRep;
+ /* Need to increment the ref counts */
+ Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
+ Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
}
/* Note: The object *must* be in dict-sugar format */
@@ -4912,18 +4930,6 @@ static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
return resObjPtr;
}
-static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
-{
- Jim_Obj *resultObjPtr;
-
- if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) {
- /* Note that the result has a ref count of 1, but we need a ref count of 0 */
- resultObjPtr->refCount--;
- return resultObjPtr;
- }
- return NULL;
-}
-
/* -----------------------------------------------------------------------------
* CallFrame
* ---------------------------------------------------------------------------*/
@@ -4991,8 +4997,8 @@ static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
}
else {
Jim_DeleteHashEntry(ht, fqname);
- Jim_InterpIncrProcEpoch(interp);
}
+ Jim_InterpIncrProcEpoch(interp);
}
Jim_DecrRefCount(interp, cmdNameObj);
JimFreeQualifiedName(interp, fqObjName);
@@ -5003,6 +5009,62 @@ static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
return JIM_OK;
}
+/**
+ * Run any $jim::defer scripts for the current call frame.
+ *
+ * retcode is the return code from the current proc.
+ *
+ * Returns the new return code.
+ */
+static int JimInvokeDefer(Jim_Interp *interp, int retcode)
+{
+ Jim_Obj *objPtr;
+
+ /* Fast check for the likely case that the variable doesn't exist */
+ if (Jim_FindHashEntry(&interp->framePtr->vars, "jim::defer") == NULL) {
+ return retcode;
+ }
+
+ objPtr = Jim_GetVariableStr(interp, "jim::defer", JIM_NONE);
+
+ if (objPtr) {
+ int ret = JIM_OK;
+ int i;
+ int listLen = Jim_ListLength(interp, objPtr);
+ Jim_Obj *resultObjPtr;
+
+ Jim_IncrRefCount(objPtr);
+
+ /* Need to save away the current interp result and
+ * restore it if appropriate
+ */
+ resultObjPtr = Jim_GetResult(interp);
+ Jim_IncrRefCount(resultObjPtr);
+ Jim_SetEmptyResult(interp);
+
+ /* Invoke in reverse order */
+ for (i = listLen; i > 0; i--) {
+ /* If a defer script returns an error, don't evaluate remaining scripts */
+ Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
+ ret = Jim_EvalObj(interp, scriptObjPtr);
+ if (ret != JIM_OK) {
+ break;
+ }
+ }
+
+ if (ret == JIM_OK || retcode == JIM_ERR) {
+ /* defer script had no error, or proc had an error so restore proc result */
+ Jim_SetResult(interp, resultObjPtr);
+ }
+ else {
+ retcode = ret;
+ }
+
+ Jim_DecrRefCount(interp, resultObjPtr);
+ Jim_DecrRefCount(interp, objPtr);
+ }
+ return retcode;
+}
#define JIM_FCF_FULL 0 /* Always free the vars hash table */
#define JIM_FCF_REUSE 1 /* Reuse the vars hash table if possible */
@@ -5308,11 +5370,11 @@ int Jim_Collect(Jim_Interp *interp)
Jim_Obj *objPtr;
/* Avoid recursive calls */
- if (interp->lastCollectId == -1) {
+ if (interp->lastCollectId == (unsigned long)~0) {
/* Jim_Collect() already running. Return just now. */
return 0;
}
- interp->lastCollectId = -1;
+ interp->lastCollectId = ~0;
/* Mark all the references found into the 'mark' hash table.
* The references are searched in every live object that
@@ -5523,6 +5585,8 @@ void Jim_FreeInterp(Jim_Interp *i)
/* Free the active call frames list - must be done before i->commands is destroyed */
for (cf = i->framePtr; cf; cf = cfx) {
+ /* Note that we ignore any errors */
+ JimInvokeDefer(i, JIM_OK);
cfx = cf->parent;
JimFreeCallFrame(i, cf, JIM_FCF_FULL);
}
@@ -5555,6 +5619,7 @@ void Jim_FreeInterp(Jim_Interp *i)
printf("Objects still in the free list:\n");
while (objPtr) {
const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
+ Jim_String(objPtr);
if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
printf("%p (%d) %-10s: '%.20s...'\n",
@@ -6011,11 +6076,6 @@ static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
jim_wide wideValue;
const char *str;
- /* Preserve the string representation.
- * Needed so we can convert back to int without loss
- */
- str = Jim_String(objPtr);
-
#ifdef HAVE_LONG_LONG
/* Assume a 53 bit mantissa */
#define MIN_INT_IN_DOUBLE -(1LL << 53)
@@ -6029,8 +6089,12 @@ static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
objPtr->typePtr = &coercedDoubleObjType;
return JIM_OK;
}
- else
#endif
+ /* Preserve the string representation.
+ * Needed so we can convert back to int without loss
+ */
+ str = Jim_String(objPtr);
+
if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
/* Managed to convert to an int, so we can use this as a cooerced double */
Jim_FreeIntRep(interp, objPtr);
@@ -6657,8 +6721,12 @@ static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs,
}
ele[dst] = ele[src];
}
- /* At end of list, keep the final element */
- ele[++dst] = ele[src];
+
+ /* At end of list, keep the final element unless all elements were kept */
+ dst++;
+ if (dst < listObjPtr->internalRep.listValue.len) {
+ ele[dst] = ele[src];
+ }
/* Set the new length */
listObjPtr->internalRep.listValue.len = dst;
@@ -7247,8 +7315,10 @@ int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
}
return JIM_ERR;
}
- *objPtrPtr = he->u.val;
- return JIM_OK;
+ else {
+ *objPtrPtr = Jim_GetHashEntryVal(he);
+ return JIM_OK;
+ }
}
/* Return an allocated array of key/value pairs for the dictionary. Stores the length in *len */
@@ -7509,7 +7579,7 @@ static const char * const jimReturnCodes[] = {
NULL
};
-#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes))
+#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)
static const Jim_ObjType returnCodeObjType = {
"return-code",
@@ -7567,13 +7637,12 @@ static int JimParseExprNumber(struct JimParserCtx *pc);
static int JimParseExprIrrational(struct JimParserCtx *pc);
static int JimParseExprBoolean(struct JimParserCtx *pc);
-/* Exrp's Stack machine operators opcodes. */
-
-/* Binary operators (numbers) */
+/* expr operator opcodes. */
enum
{
/* Continues on from the JIM_TT_ space */
- /* Operations */
+
+ /* Binary operators (numbers) */
JIM_EXPROP_MUL = JIM_TT_EXPR_OP, /* 20 */
JIM_EXPROP_DIV,
JIM_EXPROP_MOD,
@@ -7592,45 +7661,26 @@ enum
JIM_EXPROP_BITAND, /* 35 */
JIM_EXPROP_BITXOR,
JIM_EXPROP_BITOR,
-
- /* Note must keep these together */
JIM_EXPROP_LOGICAND, /* 38 */
- JIM_EXPROP_LOGICAND_LEFT,
- JIM_EXPROP_LOGICAND_RIGHT,
-
- /* and these */
- JIM_EXPROP_LOGICOR, /* 41 */
- JIM_EXPROP_LOGICOR_LEFT,
- JIM_EXPROP_LOGICOR_RIGHT,
-
- /* and these */
- /* Ternary operators */
- JIM_EXPROP_TERNARY, /* 44 */
- JIM_EXPROP_TERNARY_LEFT,
- JIM_EXPROP_TERNARY_RIGHT,
+ JIM_EXPROP_LOGICOR, /* 39 */
+ JIM_EXPROP_TERNARY, /* 40 */
+ JIM_EXPROP_COLON, /* 41 */
+ JIM_EXPROP_POW, /* 42 */
- /* and these */
- JIM_EXPROP_COLON, /* 47 */
- JIM_EXPROP_COLON_LEFT,
- JIM_EXPROP_COLON_RIGHT,
-
- JIM_EXPROP_POW, /* 50 */
-
-/* Binary operators (strings) */
- JIM_EXPROP_STREQ, /* 51 */
+ /* Binary operators (strings) */
+ JIM_EXPROP_STREQ, /* 43 */
JIM_EXPROP_STRNE,
JIM_EXPROP_STRIN,
JIM_EXPROP_STRNI,
-/* Unary operators (numbers) */
- JIM_EXPROP_NOT, /* 55 */
+ /* Unary operators (numbers) */
+ JIM_EXPROP_NOT, /* 47 */
JIM_EXPROP_BITNOT,
JIM_EXPROP_UNARYMINUS,
JIM_EXPROP_UNARYPLUS,
/* Functions */
- JIM_EXPROP_FUNC_FIRST, /* 59 */
- JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
+ JIM_EXPROP_FUNC_INT, /* 51 */
JIM_EXPROP_FUNC_WIDE,
JIM_EXPROP_FUNC_ABS,
JIM_EXPROP_FUNC_DOUBLE,
@@ -7660,46 +7710,47 @@ enum
JIM_EXPROP_FUNC_FMOD,
};
-struct JimExprState
-{
- Jim_Obj **stack;
- int stacklen;
- int opcode;
- int skip;
+/* A expression node is either a term or an operator
+ * If a node is an operator, 'op' points to the details of the operator and it's terms.
+ */
+struct JimExprNode {
+ int type; /* JIM_TT_xxx */
+ struct Jim_Obj *objPtr; /* The object for a term, or NULL for an operator */
+
+ struct JimExprNode *left; /* For all operators */
+ struct JimExprNode *right; /* For binary operators */
+ struct JimExprNode *ternary; /* For ternary operator only */
};
/* Operators table */
typedef struct Jim_ExprOperator
{
const char *name;
- int (*funcop) (Jim_Interp *interp, struct JimExprState * e);
+ int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
unsigned char precedence;
unsigned char arity;
- unsigned char lazy;
+ unsigned char attr;
unsigned char namelen;
} Jim_ExprOperator;
-static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
-{
- Jim_IncrRefCount(obj);
- e->stack[e->stacklen++] = obj;
-}
-
-static Jim_Obj *ExprPop(struct JimExprState *e)
-{
- return e->stack[--e->stacklen];
-}
+static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
+static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
+static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
-static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
{
int intresult = 1;
- int rc = JIM_OK;
- Jim_Obj *A = ExprPop(e);
+ int rc;
double dA, dC = 0;
jim_wide wA, wC = 0;
+ Jim_Obj *A;
+
+ if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
+ return rc;
+ }
if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
- switch (e->opcode) {
+ switch (node->type) {
case JIM_EXPROP_FUNC_INT:
case JIM_EXPROP_FUNC_WIDE:
case JIM_EXPROP_FUNC_ROUND:
@@ -7724,7 +7775,7 @@ static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
}
}
else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
- switch (e->opcode) {
+ switch (node->type) {
case JIM_EXPROP_FUNC_INT:
case JIM_EXPROP_FUNC_WIDE:
wC = dA;
@@ -7759,10 +7810,10 @@ static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
if (rc == JIM_OK) {
if (intresult) {
- ExprPush(e, Jim_NewIntObj(interp, wC));
+ Jim_SetResultInt(interp, wC);
}
else {
- ExprPush(e, Jim_NewDoubleObj(interp, dC));
+ Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
}
}
@@ -7779,20 +7830,25 @@ static double JimRandDouble(Jim_Interp *interp)
return (double)x / (unsigned long)~0;
}
-static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
{
- Jim_Obj *A = ExprPop(e);
jim_wide wA;
+ Jim_Obj *A;
+ int rc;
+
+ if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
+ return rc;
+ }
- int rc = Jim_GetWide(interp, A, &wA);
+ rc = Jim_GetWide(interp, A, &wA);
if (rc == JIM_OK) {
- switch (e->opcode) {
+ switch (node->type) {
case JIM_EXPROP_BITNOT:
- ExprPush(e, Jim_NewIntObj(interp, ~wA));
+ Jim_SetResultInt(interp, ~wA);
break;
case JIM_EXPROP_FUNC_SRAND:
JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
- ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
+ Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
break;
default:
abort();
@@ -7804,25 +7860,29 @@ static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
return rc;
}
-static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
{
- JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
+ JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
- ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
+ Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
return JIM_OK;
}
#ifdef JIM_MATH_FUNCTIONS
-static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
{
int rc;
- Jim_Obj *A = ExprPop(e);
double dA, dC;
+ Jim_Obj *A;
+
+ if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
+ return rc;
+ }
rc = Jim_GetDouble(interp, A, &dA);
if (rc == JIM_OK) {
- switch (e->opcode) {
+ switch (node->type) {
case JIM_EXPROP_FUNC_SIN:
dC = sin(dA);
break;
@@ -7871,7 +7931,7 @@ static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
default:
abort();
}
- ExprPush(e, Jim_NewDoubleObj(interp, dC));
+ Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
}
Jim_DecrRefCount(interp, A);
@@ -7881,19 +7941,28 @@ static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
#endif
/* A binary operation on two ints */
-static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
{
- Jim_Obj *B = ExprPop(e);
- Jim_Obj *A = ExprPop(e);
jim_wide wA, wB;
- int rc = JIM_ERR;
+ int rc;
+ Jim_Obj *A, *B;
+
+ if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
+ return rc;
+ }
+ if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
+ Jim_DecrRefCount(interp, A);
+ return rc;
+ }
+
+ rc = JIM_ERR;
if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
jim_wide wC;
rc = JIM_OK;
- switch (e->opcode) {
+ switch (node->type) {
case JIM_EXPROP_LSHIFT:
wC = wA << wB;
break;
@@ -7950,7 +8019,7 @@ static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
/* Shift left by the word size or more is undefined. */
uB %= S;
- if (e->opcode == JIM_EXPROP_ROTR) {
+ if (node->type == JIM_EXPROP_ROTR) {
uB = S - uB;
}
wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
@@ -7959,8 +8028,7 @@ static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
default:
abort();
}
- ExprPush(e, Jim_NewIntObj(interp, wC));
-
+ Jim_SetResultInt(interp, wC);
}
Jim_DecrRefCount(interp, A);
@@ -7971,14 +8039,20 @@ static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
/* A binary operation on two ints or two doubles (or two strings for some ops) */
-static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
{
int rc = JIM_OK;
double dA, dB, dC = 0;
jim_wide wA, wB, wC = 0;
+ Jim_Obj *A, *B;
- Jim_Obj *B = ExprPop(e);
- Jim_Obj *A = ExprPop(e);
+ if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
+ return rc;
+ }
+ if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
+ Jim_DecrRefCount(interp, A);
+ return rc;
+ }
if ((A->typePtr != &doubleObjType || A->bytes) &&
(B->typePtr != &doubleObjType || B->bytes) &&
@@ -7986,7 +8060,7 @@ static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
/* Both are ints */
- switch (e->opcode) {
+ switch (node->type) {
case JIM_EXPROP_POW:
case JIM_EXPROP_FUNC_POW:
if (wA == 0 && wB < 0) {
@@ -8051,7 +8125,7 @@ static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
}
}
if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
- switch (e->opcode) {
+ switch (node->type) {
#ifndef JIM_MATH_FUNCTIONS
case JIM_EXPROP_POW:
case JIM_EXPROP_FUNC_POW:
@@ -8123,7 +8197,7 @@ static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
/* XXX: Could optimise the eq/ne case by checking lengths */
int i = Jim_StringCompareObj(interp, A, B, 0);
- switch (e->opcode) {
+ switch (node->type) {
case JIM_EXPROP_LT:
wC = i < 0;
goto intresult;
@@ -8151,10 +8225,10 @@ done:
Jim_DecrRefCount(interp, B);
return rc;
intresult:
- ExprPush(e, Jim_NewIntObj(interp, wC));
+ Jim_SetResultInt(interp, wC);
goto done;
doubleresult:
- ExprPush(e, Jim_NewDoubleObj(interp, dC));
+ Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
goto done;
}
@@ -8172,18 +8246,27 @@ static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valOb
return 0;
}
-static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
-{
- Jim_Obj *B = ExprPop(e);
- Jim_Obj *A = ExprPop(e);
+
+static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
+{
+ Jim_Obj *A, *B;
jim_wide wC;
+ int rc;
+
+ if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
+ return rc;
+ }
+ if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
+ Jim_DecrRefCount(interp, A);
+ return rc;
+ }
- switch (e->opcode) {
+ switch (node->type) {
case JIM_EXPROP_STREQ:
case JIM_EXPROP_STRNE:
wC = Jim_StringEqObj(A, B);
- if (e->opcode == JIM_EXPROP_STRNE) {
+ if (node->type == JIM_EXPROP_STRNE) {
wC = !wC;
}
break;
@@ -8196,12 +8279,12 @@ static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
default:
abort();
}
- ExprPush(e, Jim_NewIntObj(interp, wC));
+ Jim_SetResultInt(interp, wC);
Jim_DecrRefCount(interp, A);
Jim_DecrRefCount(interp, B);
- return JIM_OK;
+ return rc;
}
static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
@@ -8209,162 +8292,78 @@ static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
long l;
double d;
int b;
+ int ret = -1;
+
+ /* In case the object is interp->result with refcount 1*/
+ Jim_IncrRefCount(obj);
if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
- return l != 0;
- }
- if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
- return d != 0;
+ ret = (l != 0);
}
- if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
- return b != 0;
+ else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
+ ret = (d != 0);
}
- return -1;
-}
-
-static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e)
-{
- Jim_Obj *skip = ExprPop(e);
- Jim_Obj *A = ExprPop(e);
- int rc = JIM_OK;
-
- switch (ExprBool(interp, A)) {
- case 0:
- /* false, so skip RHS opcodes with a 0 result */
- e->skip = JimWideValue(skip);
- ExprPush(e, Jim_NewIntObj(interp, 0));
- break;
-
- case 1:
- /* true so continue */
- break;
-
- case -1:
- /* Invalid */
- rc = JIM_ERR;
+ else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
+ ret = (b != 0);
}
- Jim_DecrRefCount(interp, A);
- Jim_DecrRefCount(interp, skip);
- return rc;
+ Jim_DecrRefCount(interp, obj);
+ return ret;
}
-static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
{
- Jim_Obj *skip = ExprPop(e);
- Jim_Obj *A = ExprPop(e);
- int rc = JIM_OK;
-
- switch (ExprBool(interp, A)) {
- case 0:
- /* false, so do nothing */
- break;
+ /* evaluate left */
+ int result = JimExprGetTermBoolean(interp, node->left);
- case 1:
- /* true so skip RHS opcodes with a 1 result */
- e->skip = JimWideValue(skip);
- ExprPush(e, Jim_NewIntObj(interp, 1));
- break;
-
- case -1:
- /* Invalid */
- rc = JIM_ERR;
- break;
+ if (result == 1) {
+ /* true so evaluate right */
+ result = JimExprGetTermBoolean(interp, node->right);
}
- Jim_DecrRefCount(interp, A);
- Jim_DecrRefCount(interp, skip);
-
- return rc;
-}
-
-static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e)
-{
- Jim_Obj *A = ExprPop(e);
- int rc = JIM_OK;
-
- switch (ExprBool(interp, A)) {
- case 0:
- ExprPush(e, Jim_NewIntObj(interp, 0));
- break;
-
- case 1:
- ExprPush(e, Jim_NewIntObj(interp, 1));
- break;
-
- case -1:
- /* Invalid */
- rc = JIM_ERR;
- break;
+ if (result == -1) {
+ return JIM_ERR;
}
- Jim_DecrRefCount(interp, A);
-
- return rc;
+ Jim_SetResultInt(interp, result);
+ return JIM_OK;
}
-static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
{
- Jim_Obj *skip = ExprPop(e);
- Jim_Obj *A = ExprPop(e);
- int rc = JIM_OK;
-
- /* Repush A */
- ExprPush(e, A);
-
- switch (ExprBool(interp, A)) {
- case 0:
- /* false, skip RHS opcodes */
- e->skip = JimWideValue(skip);
- /* Push a dummy value */
- ExprPush(e, Jim_NewIntObj(interp, 0));
- break;
+ /* evaluate left */
+ int result = JimExprGetTermBoolean(interp, node->left);
- case 1:
- /* true so do nothing */
- break;
-
- case -1:
- /* Invalid */
- rc = JIM_ERR;
- break;
+ if (result == 0) {
+ /* false so evaluate right */
+ result = JimExprGetTermBoolean(interp, node->right);
}
- Jim_DecrRefCount(interp, A);
- Jim_DecrRefCount(interp, skip);
-
- return rc;
-}
-
-static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e)
-{
- Jim_Obj *skip = ExprPop(e);
- Jim_Obj *B = ExprPop(e);
- Jim_Obj *A = ExprPop(e);
-
- /* No need to check for A as non-boolean */
- if (ExprBool(interp, A)) {
- /* true, so skip RHS opcodes */
- e->skip = JimWideValue(skip);
- /* Repush B as the answer */
- ExprPush(e, B);
+ if (result == -1) {
+ return JIM_ERR;
}
-
- Jim_DecrRefCount(interp, skip);
- Jim_DecrRefCount(interp, A);
- Jim_DecrRefCount(interp, B);
+ Jim_SetResultInt(interp, result);
return JIM_OK;
}
-static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e)
+static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
{
- return JIM_OK;
+ /* evaluate left */
+ int result = JimExprGetTermBoolean(interp, node->left);
+
+ if (result == 1) {
+ /* true so select right */
+ return JimExprEvalTermNode(interp, node->right);
+ }
+ else if (result == 0) {
+ /* false so select ternary */
+ return JimExprEvalTermNode(interp, node->ternary);
+ }
+ /* error */
+ return JIM_ERR;
}
enum
{
- LAZY_NONE,
- LAZY_OP,
- LAZY_LEFT,
- LAZY_RIGHT,
- RIGHT_ASSOC, /* reuse this field for right associativity too */
+ OP_FUNC = 0x0001, /* function syntax */
+ OP_RIGHT_ASSOC = 0x0002, /* right associative */
};
/* name - precedence - arity - opcode
@@ -8374,7 +8373,7 @@ enum
* The following macros pre-compute the string length at compile time.
*/
#define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
-#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, LAZY_NONE)
+#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)
static const struct Jim_ExprOperator Jim_ExprOperators[] = {
OPRINIT("*", 110, 2, JimExprOpBin),
@@ -8402,24 +8401,13 @@ static const struct Jim_ExprOperator Jim_ExprOperators[] = {
OPRINIT("^", 49, 2, JimExprOpIntBin),
OPRINIT("|", 48, 2, JimExprOpIntBin),
- OPRINIT_ATTR("&&", 10, 2, NULL, LAZY_OP),
- OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT),
- OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT),
-
- OPRINIT_ATTR("||", 9, 2, NULL, LAZY_OP),
- OPRINIT_ATTR(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT),
- OPRINIT_ATTR(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT),
-
- OPRINIT_ATTR("?", 5, 2, JimExprOpNull, LAZY_OP),
- OPRINIT_ATTR(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT),
- OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
-
- OPRINIT_ATTR(":", 5, 2, JimExprOpNull, LAZY_OP),
- OPRINIT_ATTR(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT),
- OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
+ OPRINIT("&&", 10, 2, JimExprOpAnd),
+ OPRINIT("||", 9, 2, JimExprOpOr),
+ OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
+ OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
/* Precedence is higher than * and / but lower than ! and ~, and right-associative */
- OPRINIT_ATTR("**", 120, 2, JimExprOpBin, RIGHT_ASSOC),
+ OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
OPRINIT("eq", 60, 2, JimExprOpStrBin),
OPRINIT("ne", 60, 2, JimExprOpStrBin),
@@ -8427,45 +8415,45 @@ static const struct Jim_ExprOperator Jim_ExprOperators[] = {
OPRINIT("in", 55, 2, JimExprOpStrBin),
OPRINIT("ni", 55, 2, JimExprOpStrBin),
- OPRINIT("!", 150, 1, JimExprOpNumUnary),
- OPRINIT("~", 150, 1, JimExprOpIntUnary),
- OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
- OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
+ OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
+ OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
+ OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
+ OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
- OPRINIT("int", 200, 1, JimExprOpNumUnary),
- OPRINIT("wide", 200, 1, JimExprOpNumUnary),
- OPRINIT("abs", 200, 1, JimExprOpNumUnary),
- OPRINIT("double", 200, 1, JimExprOpNumUnary),
- OPRINIT("round", 200, 1, JimExprOpNumUnary),
- OPRINIT("rand", 200, 0, JimExprOpNone),
- OPRINIT("srand", 200, 1, JimExprOpIntUnary),
+ OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
+ OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
+ OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
+ OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
+ OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
+ OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
+ OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),
#ifdef JIM_MATH_FUNCTIONS
- OPRINIT("sin", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("cos", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("tan", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("asin", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("acos", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("atan", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("atan2", 200, 2, JimExprOpBin),
- OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("floor", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("exp", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("log", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("log10", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary),
- OPRINIT("pow", 200, 2, JimExprOpBin),
- OPRINIT("hypot", 200, 2, JimExprOpBin),
- OPRINIT("fmod", 200, 2, JimExprOpBin),
+ OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
+ OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
+ OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
+ OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
+ OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
#endif
};
#undef OPRINIT
-#undef OPRINIT_LAZY
+#undef OPRINIT_ATTR
#define JIM_EXPR_OPERATORS_NUM \
(sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
@@ -8624,31 +8612,40 @@ static int JimParseExprBoolean(struct JimParserCtx *pc)
return JIM_ERR;
}
+static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
+{
+ static Jim_ExprOperator dummy_op;
+ if (opcode < JIM_TT_EXPR_OP) {
+ return &dummy_op;
+ }
+ return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
+}
+
static int JimParseExprOperator(struct JimParserCtx *pc)
{
int i;
- int bestIdx = -1, bestLen = 0;
+ const struct Jim_ExprOperator *bestOp = NULL;
+ int bestLen = 0;
/* Try to get the longest match. */
for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
- const char * const opname = Jim_ExprOperators[i].name;
- const int oplen = Jim_ExprOperators[i].namelen;
+ const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];
- if (opname == NULL || opname[0] != pc->p[0]) {
+ if (op->name[0] != pc->p[0]) {
continue;
}
- if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
- bestIdx = i + JIM_TT_EXPR_OP;
- bestLen = oplen;
+ if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
+ bestOp = op;
+ bestLen = op->namelen;
}
}
- if (bestIdx == -1) {
+ if (bestOp == NULL) {
return JIM_ERR;
}
/* Validate paretheses around function arguments */
- if (bestIdx >= JIM_EXPROP_FUNC_FIRST) {
+ if (bestOp->attr & OP_FUNC) {
const char *p = pc->p + bestLen;
int len = pc->len - bestLen;
@@ -8664,19 +8661,10 @@ static int JimParseExprOperator(struct JimParserCtx *pc)
pc->p += bestLen;
pc->len -= bestLen;
- pc->tt = bestIdx;
+ pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
return JIM_OK;
}
-static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
-{
- static Jim_ExprOperator dummy_op;
- if (opcode < JIM_TT_EXPR_OP) {
- return &dummy_op;
- }
- return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
-}
-
const char *jim_tt_name(int type)
{
static const char * const tt_names[JIM_TT_EXPR_OP] =
@@ -8718,35 +8706,42 @@ static const Jim_ObjType exprObjType = {
JIM_TYPE_REFERENCES,
};
-/* Expr bytecode structure */
-typedef struct ExprByteCode
+/* expr tree structure */
+struct ExprTree
{
- ScriptToken *token; /* Tokens array. */
- int len; /* Length as number of tokens. */
+ struct JimExprNode *expr; /* The first operator or term */
+ struct JimExprNode *nodes; /* Storage of all nodes in the tree */
+ int len; /* Number of nodes in use */
int inUse; /* Used for sharing. */
-} ExprByteCode;
+};
-static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)
+static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
{
int i;
-
- for (i = 0; i < expr->len; i++) {
- Jim_DecrRefCount(interp, expr->token[i].objPtr);
+ for (i = 0; i < num; i++) {
+ if (nodes[i].objPtr) {
+ Jim_DecrRefCount(interp, nodes[i].objPtr);
+ }
}
- Jim_Free(expr->token);
+ Jim_Free(nodes);
+}
+
+static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
+{
+ ExprTreeFreeNodes(interp, expr->nodes, expr->len);
Jim_Free(expr);
}
static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
- ExprByteCode *expr = (void *)objPtr->internalRep.ptr;
+ struct ExprTree *expr = (void *)objPtr->internalRep.ptr;
if (expr) {
if (--expr->inUse != 0) {
return;
}
- ExprFreeByteCode(interp, expr);
+ ExprTreeFree(interp, expr);
}
}
@@ -8759,376 +8754,156 @@ static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dup
dupPtr->typePtr = NULL;
}
-/* Check if an expr program looks correct
- * Sets an error result on invalid
- */
-static int ExprCheckCorrectness(Jim_Interp *interp, Jim_Obj *exprObjPtr, ExprByteCode * expr)
+struct ExprBuilder {
+ int parencount; /* count of outstanding parentheses */
+ int level; /* recursion depth */
+ ParseToken *token; /* The current token */
+ ParseToken *first_token; /* The first token */
+ Jim_Stack stack; /* stack of pending terms */
+ Jim_Obj *exprObjPtr; /* the original expression */
+ Jim_Obj *fileNameObj; /* filename of the original expression */
+ struct JimExprNode *nodes; /* storage for all nodes */
+ struct JimExprNode *next; /* storage for the next node */
+};
+
+#ifdef DEBUG_SHOW_EXPR
+static void JimShowExprNode(struct JimExprNode *node, int level)
{
int i;
- int stacklen = 0;
- int ternary = 0;
- int lasttt = JIM_TT_NONE;
- const char *errmsg;
-
- /* Try to check if there are stack underflows,
- * and make sure at the end of the program there is
- * a single result on the stack. */
- for (i = 0; i < expr->len; i++) {
- ScriptToken *t = &expr->token[i];
- const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
- lasttt = t->type;
-
- stacklen -= op->arity;
-
- if (stacklen < 0) {
- break;
- }
- if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) {
- ternary++;
- }
- else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) {
- ternary--;
- }
-
- /* All operations and operands add one to the stack */
- stacklen++;
- }
- if (stacklen == 1 && ternary == 0) {
- return JIM_OK;
+ for (i = 0; i < level; i++) {
+ printf(" ");
}
-
- if (stacklen <= 0) {
- /* Too few args */
- if (lasttt >= JIM_EXPROP_FUNC_FIRST) {
- errmsg = "too few arguments for math function";
- Jim_SetResultString(interp, "too few arguments for math function", -1);
- } else {
- errmsg = "premature end of expression";
+ if (TOKEN_IS_EXPR_OP(node->type)) {
+ printf("%s\n", jim_tt_name(node->type));
+ if (node->left) {
+ JimShowExprNode(node->left, level + 1);
}
- }
- else if (stacklen > 1) {
- if (lasttt >= JIM_EXPROP_FUNC_FIRST) {
- errmsg = "too many arguments for math function";
- } else {
- errmsg = "extra tokens at end of expression";
+ if (node->right) {
+ JimShowExprNode(node->right, level + 1);
+ }
+ if (node->ternary) {
+ JimShowExprNode(node->ternary, level + 1);
}
}
else {
- errmsg = "invalid ternary expression";
+ printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
}
- Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": %s", exprObjPtr, errmsg);
- return JIM_ERR;
}
+#endif
-/* This procedure converts every occurrence of || and && opereators
- * in lazy unary versions.
- *
- * a b || is converted into:
- *
- * a <offset> |L b |R
- *
- * a b && is converted into:
+#define EXPR_UNTIL_CLOSE 0x0001
+#define EXPR_FUNC_ARGS 0x0002
+#define EXPR_TERNARY 0x0004
+
+/**
+ * Parse the subexpression at builder->token and return with the node on the stack.
+ * builder->token is advanced to the next unconsumed token.
+ * Returns JIM_OK if OK or JIM_ERR on error and leaves a message in the interpreter result.
*
- * a <offset> &L b &R
+ * 'precedence' is the precedence of the current operator. Tokens are consumed until an operator
+ * with an equal or lower precedence is reached (or strictly lower if right associative).
*
- * "|L" checks if 'a' is true:
- * 1) if it is true pushes 1 and skips <offset> instructions to reach
- * the opcode just after |R.
- * 2) if it is false does nothing.
- * "|R" checks if 'b' is true:
- * 1) if it is true pushes 1, otherwise pushes 0.
+ * If EXPR_UNTIL_CLOSE is set, the subexpression extends up to and including the next close parenthesis.
+ * If EXPR_FUNC_ARGS is set, multiple subexpressions (terms) are expected separated by comma
+ * If EXPR_TERNARY is set, two subexpressions (terms) are expected separated by colon
*
- * "&L" checks if 'a' is true:
- * 1) if it is true does nothing.
- * 2) If it is false pushes 0 and skips <offset> instructions to reach
- * the opcode just after &R
- * "&R" checks if 'a' is true:
- * if it is true pushes 1, otherwise pushes 0.
+ * 'exp_numterms' indicates how many terms are expected. Normally this is 1, but may be more for EXPR_FUNC_ARGS and EXPR_TERNARY.
*/
-static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
-{
- int i;
-
- int leftindex, arity, offset;
-
- /* Search for the end of the first operator */
- leftindex = expr->len - 1;
-
- arity = 1;
- while (arity) {
- ScriptToken *tt = &expr->token[leftindex];
-
- if (tt->type >= JIM_TT_EXPR_OP) {
- arity += JimExprOperatorInfoByOpcode(tt->type)->arity;
- }
- arity--;
- if (--leftindex < 0) {
- return JIM_ERR;
- }
- }
- leftindex++;
-
- /* Move them up */
- memmove(&expr->token[leftindex + 2], &expr->token[leftindex],
- sizeof(*expr->token) * (expr->len - leftindex));
- expr->len += 2;
- offset = (expr->len - leftindex) - 1;
-
- /* Now we rely on the fact that the left and right version have opcodes
- * 1 and 2 after the main opcode respectively
- */
- expr->token[leftindex + 1].type = t->type + 1;
- expr->token[leftindex + 1].objPtr = interp->emptyObj;
-
- expr->token[leftindex].type = JIM_TT_EXPR_INT;
- expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset);
-
- /* Now add the 'R' operator */
- expr->token[expr->len].objPtr = interp->emptyObj;
- expr->token[expr->len].type = t->type + 2;
- expr->len++;
-
- /* Do we need to adjust the skip count for any &L, |L, ?L or :L in the left operand? */
- for (i = leftindex - 1; i > 0; i--) {
- const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type);
- if (op->lazy == LAZY_LEFT) {
- if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) {
- JimWideValue(expr->token[i - 1].objPtr) += 2;
- }
- }
- }
- return JIM_OK;
-}
-
-static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
+static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms)
{
- struct ScriptToken *token = &expr->token[expr->len];
- const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
+ int rc;
+ struct JimExprNode *node;
+ /* Calculate the stack length expected after pushing the number of expected terms */
+ int exp_stacklen = builder->stack.len + exp_numterms;
- if (op->lazy == LAZY_OP) {
- if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) {
- Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name);
- return JIM_ERR;
- }
- }
- else {
- token->objPtr = interp->emptyObj;
- token->type = t->type;
- expr->len++;
+ if (builder->level++ > 200) {
+ Jim_SetResultString(interp, "Expression too complex", -1);
+ return JIM_ERR;
}
- return JIM_OK;
-}
-/**
- * Returns the index of the COLON_LEFT to the left of 'right_index'
- * taking into account nesting.
- *
- * The expression *must* be well formed, thus a COLON_LEFT will always be found.
- */
-static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index)
-{
- int ternary_count = 1;
+ while (builder->token->type != JIM_TT_EOL) {
+ ParseToken *t = builder->token++;
+ int prevtt;
- right_index--;
-
- while (right_index > 1) {
- if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) {
- ternary_count--;
- }
- else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) {
- ternary_count++;
+ if (t == builder->first_token) {
+ prevtt = JIM_TT_NONE;
}
- else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) {
- return right_index;
+ else {
+ prevtt = t[-1].type;
}
- right_index--;
- }
-
- /*notreached*/
- return -1;
-}
-
-/**
- * Find the left/right indices for the ternary expression to the left of 'right_index'.
- *
- * Returns 1 if found, and fills in *prev_right_index and *prev_left_index.
- * Otherwise returns 0.
- */
-static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index)
-{
- int i = right_index - 1;
- int ternary_count = 1;
- while (i > 1) {
- if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) {
- if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) {
- *prev_right_index = i - 2;
- *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index);
- return 1;
+ if (t->type == JIM_TT_SUBEXPR_START) {
+ if (builder->stack.len == exp_stacklen) {
+ Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
+ return JIM_ERR;
}
- }
- else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) {
- if (ternary_count == 0) {
- return 0;
+ builder->parencount++;
+ rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
+ if (rc != JIM_OK) {
+ return rc;
}
- ternary_count++;
- }
- i--;
- }
- return 0;
-}
-
-/*
-* ExprTernaryReorderExpression description
-* ========================================
-*
-* ?: is right-to-left associative which doesn't work with the stack-based
-* expression engine. The fix is to reorder the bytecode.
-*
-* The expression:
-*
-* expr 1?2:0?3:4
-*
-* Has initial bytecode:
-*
-* '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '0' (44=COLON_RIGHT)
-* '2' (40=TERNARY_LEFT) '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT)
-*
-* The fix involves simulating this expression instead:
-*
-* expr 1?2:(0?3:4)
-*
-* With the following bytecode:
-*
-* '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '10' (43=COLON_LEFT) '0' '2' (40=TERNARY_LEFT)
-* '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT) (44=COLON_RIGHT)
-*
-* i.e. The token COLON_RIGHT at index 8 is moved towards the end of the stack, all tokens above 8
-* are shifted down and the skip count of the token JIM_EXPROP_COLON_LEFT at index 5 is
-* incremented by the amount tokens shifted down. The token JIM_EXPROP_COLON_RIGHT that is moved
-* is identified as immediately preceeding a token JIM_EXPROP_TERNARY_LEFT
-*
-* ExprTernaryReorderExpression works thus as follows :
-* - start from the end of the stack
-* - while walking towards the beginning of the stack
-* if token=JIM_EXPROP_COLON_RIGHT then
-* find the associated token JIM_EXPROP_TERNARY_LEFT, which allows to
-* find the associated token previous(JIM_EXPROP_COLON_RIGHT)
-* find the associated token previous(JIM_EXPROP_LEFT_RIGHT)
-* if all found then
-* perform the rotation
-* update the skip count of the token previous(JIM_EXPROP_LEFT_RIGHT)
-* end if
-* end if
-*
-* Note: care has to be taken for nested ternary constructs!!!
-*/
-static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr)
-{
- int i;
-
- for (i = expr->len - 1; i > 1; i--) {
- int prev_right_index;
- int prev_left_index;
- int j;
- ScriptToken tmp;
-
- if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) {
- continue;
+ /* A complete subexpression is on the stack */
}
-
- /* COLON_RIGHT found: get the indexes needed to move the tokens in the stack (if any) */
- if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) {
- continue;
+ else if (t->type == JIM_TT_SUBEXPR_END) {
+ if (!(flags & EXPR_UNTIL_CLOSE)) {
+ if (builder->stack.len == exp_stacklen && builder->level > 1) {
+ builder->token--;
+ builder->level--;
+ return JIM_OK;
+ }
+ Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
+ return JIM_ERR;
+ }
+ builder->parencount--;
+ if (builder->stack.len == exp_stacklen) {
+ /* Return with the expected number of subexpressions on the stack */
+ break;
+ }
}
-
- /*
- ** rotate tokens down
- **
- ** +-> [i] : JIM_EXPROP_COLON_RIGHT
- ** | | |
- ** | V V
- ** | [...] : ...
- ** | | |
- ** | V V
- ** | [...] : ...
- ** | | |
- ** | V V
- ** +- [prev_right_index] : JIM_EXPROP_COLON_RIGHT
- */
- tmp = expr->token[prev_right_index];
- for (j = prev_right_index; j < i; j++) {
- expr->token[j] = expr->token[j + 1];
- }
- expr->token[i] = tmp;
-
- /* Increment the 'skip' count associated to the previous JIM_EXPROP_COLON_LEFT token
- *
- * This is 'colon left increment' = i - prev_right_index
- *
- * [prev_left_index] : JIM_EXPROP_LEFT_RIGHT
- * [prev_left_index-1] : skip_count
- *
- */
- JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index);
-
- /* Adjust for i-- in the loop */
- i++;
- }
-}
-
-static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
-{
- Jim_Stack stack;
- ExprByteCode *expr;
- int ok = 1;
- int i;
- int prevtt = JIM_TT_NONE;
- int have_ternary = 0;
-
- /* -1 for EOL */
- int count = tokenlist->count - 1;
-
- expr = Jim_Alloc(sizeof(*expr));
- expr->inUse = 1;
- expr->len = 0;
-
- Jim_InitStack(&stack);
-
- /* Need extra bytecodes for lazy operators.
- * Also check for the ternary operator
- */
- for (i = 0; i < tokenlist->count; i++) {
- ParseToken *t = &tokenlist->list[i];
- const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
-
- if (op->lazy == LAZY_OP) {
- count += 2;
- /* Ternary is a lazy op but also needs reordering */
- if (t->type == JIM_EXPROP_TERNARY) {
- have_ternary = 1;
+ else if (t->type == JIM_TT_SUBEXPR_COMMA) {
+ if (!(flags & EXPR_FUNC_ARGS)) {
+ if (builder->stack.len == exp_stacklen) {
+ /* handle the comma back at the parent level */
+ builder->token--;
+ builder->level--;
+ return JIM_OK;
+ }
+ Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
+ return JIM_ERR;
+ }
+ else {
+ /* If we see more terms than expected, it is an error */
+ if (builder->stack.len > exp_stacklen) {
+ Jim_SetResultFormatted(interp, "too many arguments to math function");
+ return JIM_ERR;
+ }
}
+ /* just go onto the next arg */
}
- }
-
- expr->token = Jim_Alloc(sizeof(ScriptToken) * count);
-
- for (i = 0; i < tokenlist->count && ok; i++) {
- ParseToken *t = &tokenlist->list[i];
-
- /* Next token will be stored here */
- struct ScriptToken *token = &expr->token[expr->len];
-
- if (t->type == JIM_TT_EOL) {
- break;
+ else if (t->type == JIM_EXPROP_COLON) {
+ if (!(flags & EXPR_TERNARY)) {
+ if (builder->level != 1) {
+ /* handle the comma back at the parent level */
+ builder->token--;
+ builder->level--;
+ return JIM_OK;
+ }
+ Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
+ return JIM_ERR;
+ }
+ if (builder->stack.len == exp_stacklen) {
+ /* handle the comma back at the parent level */
+ builder->token--;
+ builder->level--;
+ return JIM_OK;
+ }
+ /* just go onto the next term */
}
-
- if (TOKEN_IS_EXPR_OP(t->type)) {
+ else if (TOKEN_IS_EXPR_OP(t->type)) {
const struct Jim_ExprOperator *op;
- ParseToken *tt;
/* Convert -/+ to unary minus or unary plus if necessary */
- if (prevtt == JIM_TT_NONE || prevtt == JIM_TT_SUBEXPR_START || prevtt == JIM_TT_SUBEXPR_COMMA || prevtt >= JIM_TT_EXPR_OP) {
+ if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
if (t->type == JIM_EXPROP_SUB) {
t->type = JIM_EXPROP_UNARYMINUS;
}
@@ -9139,68 +8914,85 @@ static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList
op = JimExprOperatorInfoByOpcode(t->type);
- /* Handle precedence */
- while ((tt = Jim_StackPeek(&stack)) != NULL) {
- const struct Jim_ExprOperator *tt_op =
- JimExprOperatorInfoByOpcode(tt->type);
-
- /* Note that right-to-left associativity of ?: operator is handled later.
- */
+ if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
+ /* next op is lower precedence, or equal and left associative, so done here */
+ builder->token--;
+ break;
+ }
- if (op->arity != 1 && tt_op->precedence >= op->precedence) {
- /* Don't reduce if right associative with equal precedence? */
- if (tt_op->precedence == op->precedence && tt_op->lazy == RIGHT_ASSOC) {
- break;
- }
- if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
- ok = 0;
- goto err;
- }
- Jim_StackPop(&stack);
+ if (op->attr & OP_FUNC) {
+ if (builder->token->type != JIM_TT_SUBEXPR_START) {
+ Jim_SetResultString(interp, "missing arguments for math function", -1);
+ return JIM_ERR;
}
- else {
- break;
+ builder->token++;
+ if (op->arity == 0) {
+ if (builder->token->type != JIM_TT_SUBEXPR_END) {
+ Jim_SetResultString(interp, "too many arguments for math function", -1);
+ return JIM_ERR;
+ }
+ builder->token++;
+ goto noargs;
}
+ builder->parencount++;
+
+ /* This will push left and return right */
+ rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
+ }
+ else if (t->type == JIM_EXPROP_TERNARY) {
+ /* Collect the two arguments to the ternary operator */
+ rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
+ }
+ else {
+ /* Recursively handle everything on the right until we see a precendence <= op->precedence or == and right associative
+ * and push that on the term stack
+ */
+ rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
}
- Jim_StackPush(&stack, t);
- }
- else if (t->type == JIM_TT_SUBEXPR_START) {
- Jim_StackPush(&stack, t);
- }
- else if (t->type == JIM_TT_SUBEXPR_END || t->type == JIM_TT_SUBEXPR_COMMA) {
- /* Reduce the expression back to the previous ( or , */
- ok = 0;
- while (Jim_StackLen(&stack)) {
- ParseToken *tt = Jim_StackPop(&stack);
- if (tt->type == JIM_TT_SUBEXPR_START || tt->type == JIM_TT_SUBEXPR_COMMA) {
- if (t->type == JIM_TT_SUBEXPR_COMMA) {
- /* Need to push back the previous START or COMMA in the case of comma */
- Jim_StackPush(&stack, tt);
- }
- ok = 1;
- break;
+ if (rc != JIM_OK) {
+ return rc;
+ }
+
+noargs:
+ node = builder->next++;
+ node->type = t->type;
+
+ if (op->arity >= 3) {
+ node->ternary = Jim_StackPop(&builder->stack);
+ if (node->ternary == NULL) {
+ goto missingoperand;
}
- if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
- goto err;
+ }
+ if (op->arity >= 2) {
+ node->right = Jim_StackPop(&builder->stack);
+ if (node->right == NULL) {
+ goto missingoperand;
}
}
- if (!ok) {
- Jim_SetResultFormatted(interp, "Unexpected close parenthesis in expression: \"%#s\"", exprObjPtr);
- goto err;
+ if (op->arity >= 1) {
+ node->left = Jim_StackPop(&builder->stack);
+ if (node->left == NULL) {
+missingoperand:
+ Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
+ builder->next--;
+ return JIM_ERR;
+
+ }
}
+
+ /* Now push the node */
+ Jim_StackPush(&builder->stack, node);
}
else {
Jim_Obj *objPtr = NULL;
/* This is a simple non-operator term, so create and push the appropriate object */
- token->type = t->type;
/* Two consecutive terms without an operator is invalid */
if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
- Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", exprObjPtr);
- ok = 0;
- goto err;
+ Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
+ return JIM_ERR;
}
/* Immediately create a double or int object? */
@@ -9219,66 +9011,105 @@ static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList
}
}
- if (objPtr) {
- token->objPtr = objPtr;
- }
- else {
+ if (!objPtr) {
/* Everything else is stored a simple string term */
- token->objPtr = Jim_NewStringObj(interp, t->token, t->len);
+ objPtr = Jim_NewStringObj(interp, t->token, t->len);
if (t->type == JIM_TT_CMD) {
/* Only commands need source info */
- JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line);
+ JimSetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
}
}
- expr->len++;
+
+ /* Now push a term node */
+ node = builder->next++;
+ node->objPtr = objPtr;
+ Jim_IncrRefCount(node->objPtr);
+ node->type = t->type;
+ Jim_StackPush(&builder->stack, node);
}
- prevtt = t->type;
}
- /* Reduce any remaining subexpr */
- while (Jim_StackLen(&stack)) {
- ParseToken *tt = Jim_StackPop(&stack);
+ if (builder->stack.len == exp_stacklen) {
+ builder->level--;
+ return JIM_OK;
+ }
- if (tt->type == JIM_TT_SUBEXPR_START) {
- ok = 0;
- Jim_SetResultString(interp, "Missing close parenthesis", -1);
- goto err;
+ if ((flags & EXPR_FUNC_ARGS)) {
+ Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
+ }
+ else {
+ if (builder->stack.len < exp_stacklen) {
+ if (builder->level == 0) {
+ Jim_SetResultFormatted(interp, "empty expression");
+ }
+ else {
+ Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
+ }
}
- if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
- ok = 0;
- goto err;
+ else {
+ Jim_SetResultFormatted(interp, "extra terms after expression");
}
}
- if (have_ternary) {
- ExprTernaryReorderExpression(interp, expr);
- }
+ return JIM_ERR;
+}
- err:
- /* Free the stack used for the compilation. */
- Jim_FreeStack(&stack);
+static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
+{
+ struct ExprTree *expr;
+ struct ExprBuilder builder;
+ int rc;
+ struct JimExprNode *top = NULL;
+
+ builder.parencount = 0;
+ builder.level = 0;
+ builder.token = builder.first_token = tokenlist->list;
+ builder.exprObjPtr = exprObjPtr;
+ builder.fileNameObj = fileNameObj;
+ /* The bytecode will never produce more nodes than there are tokens - 1 (for EOL)*/
+ builder.nodes = malloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
+ memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
+ builder.next = builder.nodes;
+ Jim_InitStack(&builder.stack);
- for (i = 0; i < expr->len; i++) {
- Jim_IncrRefCount(expr->token[i].objPtr);
+ rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
+
+ if (rc == JIM_OK) {
+ top = Jim_StackPop(&builder.stack);
+
+ if (builder.parencount) {
+ Jim_SetResultString(interp, "missing close parenthesis", -1);
+ rc = JIM_ERR;
+ }
}
- if (!ok) {
- ExprFreeByteCode(interp, expr);
+ /* Free the stack used for the compilation. */
+ Jim_FreeStack(&builder.stack);
+
+ if (rc != JIM_OK) {
+ ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
return NULL;
}
+ expr = Jim_Alloc(sizeof(*expr));
+ expr->inUse = 1;
+ expr->expr = top;
+ expr->nodes = builder.nodes;
+ expr->len = builder.next - builder.nodes;
+
+ assert(expr->len <= tokenlist->count - 1);
+
return expr;
}
-
/* This method takes the string representation of an expression
- * and generates a program for the Expr's stack-based VM. */
+ * and generates a program for the expr engine */
static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
int exprTextLen;
const char *exprText;
struct JimParserCtx parser;
- struct ExprByteCode *expr;
+ struct ExprTree *expr;
ParseTokenList tokenlist;
int line;
Jim_Obj *fileNameObj;
@@ -9331,7 +9162,7 @@ static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
}
/* Now create the expression bytecode from the tokenlist */
- expr = ExprCreateByteCode(interp, &tokenlist, objPtr, fileNameObj);
+ expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
/* No longer need the token list */
ScriptTokenListFree(&tokenlist);
@@ -9341,26 +9172,10 @@ static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
}
#ifdef DEBUG_SHOW_EXPR
- {
- int i;
-
- printf("==== Expr ====\n");
- for (i = 0; i < expr->len; i++) {
- ScriptToken *t = &expr->token[i];
-
- printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
- }
- }
+ printf("==== Expr ====\n");
+ JimShowExprNode(expr->expr, 0);
#endif
- /* Check program correctness. */
- if (ExprCheckCorrectness(interp, objPtr, expr) != JIM_OK) {
- /* ExprCheckCorrectness set an error in this case */
- ExprFreeByteCode(interp, expr);
- expr = NULL;
- goto err;
- }
-
rc = JIM_OK;
err:
@@ -9372,25 +9187,25 @@ static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
return rc;
}
-static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
+static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
{
if (objPtr->typePtr != &exprObjType) {
if (SetExprFromAny(interp, objPtr) != JIM_OK) {
return NULL;
}
}
- return (ExprByteCode *) Jim_GetIntRepPtr(objPtr);
+ return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
}
#ifdef JIM_OPTIMIZATION
-static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
-{
- if (token->type == JIM_TT_EXPR_INT)
- return token->objPtr;
- else if (token->type == JIM_TT_VAR)
- return Jim_GetVariable(interp, token->objPtr, JIM_NONE);
- else if (token->type == JIM_TT_DICTSUGAR)
- return JimExpandDictSugar(interp, token->objPtr);
+static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
+{
+ if (node->type == JIM_TT_EXPR_INT)
+ return node->objPtr;
+ else if (node->type == JIM_TT_VAR)
+ return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
+ else if (node->type == JIM_TT_DICTSUGAR)
+ return JimExpandDictSugar(interp, node->objPtr);
else
return NULL;
}
@@ -9398,11 +9213,11 @@ static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
/* -----------------------------------------------------------------------------
* Expressions evaluation.
- * Jim uses a specialized stack-based virtual machine for expressions,
+ * Jim uses a recursive evaluation engine for expressions,
* that takes advantage of the fact that expr's operators
* can't be redefined.
*
- * Jim_EvalExpression() uses the bytecode compiled by
+ * Jim_EvalExpression() uses the expression tree compiled by
* SetExprFromAny() method of the "expression" object.
*
* On success a Tcl Object containing the result of the evaluation
@@ -9411,15 +9226,80 @@ static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
* On error the function returns a retcode != to JIM_OK and set a suitable
* error on the interp.
* ---------------------------------------------------------------------------*/
-#define JIM_EE_STATICSTACK_LEN 10
-int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr)
+static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
{
- ExprByteCode *expr;
- Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN];
- int i;
+ if (TOKEN_IS_EXPR_OP(node->type)) {
+ const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
+ return op->funcop(interp, node);
+ }
+ else {
+ Jim_Obj *objPtr;
+
+ /* A term */
+ switch (node->type) {
+ case JIM_TT_EXPR_INT:
+ case JIM_TT_EXPR_DOUBLE:
+ case JIM_TT_EXPR_BOOLEAN:
+ case JIM_TT_STR:
+ Jim_SetResult(interp, node->objPtr);
+ return JIM_OK;
+
+ case JIM_TT_VAR:
+ objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
+ if (objPtr) {
+ Jim_SetResult(interp, objPtr);
+ return JIM_OK;
+ }
+ return JIM_ERR;
+
+ case JIM_TT_DICTSUGAR:
+ objPtr = JimExpandDictSugar(interp, node->objPtr);
+ if (objPtr) {
+ Jim_SetResult(interp, objPtr);
+ return JIM_OK;
+ }
+ return JIM_ERR;
+
+ case JIM_TT_ESC:
+ if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
+ Jim_SetResult(interp, objPtr);
+ return JIM_OK;
+ }
+ return JIM_ERR;
+
+ case JIM_TT_CMD:
+ return Jim_EvalObj(interp, node->objPtr);
+
+ default:
+ /* Should never get here */
+ return JIM_ERR;
+ }
+ }
+}
+
+static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
+{
+ int rc = JimExprEvalTermNode(interp, node);
+ if (rc == JIM_OK) {
+ *objPtrPtr = Jim_GetResult(interp);
+ Jim_IncrRefCount(*objPtrPtr);
+ }
+ return rc;
+}
+
+static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
+{
+ if (JimExprEvalTermNode(interp, node) == JIM_OK) {
+ return ExprBool(interp, Jim_GetResult(interp));
+ }
+ return -1;
+}
+
+int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
+{
+ struct ExprTree *expr;
int retcode = JIM_OK;
- struct JimExprState e;
expr = JimGetExpression(interp, exprObjPtr);
if (!expr) {
@@ -9447,35 +9327,33 @@ int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprRe
switch (expr->len) {
case 1:
- objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
+ objPtr = JimExprIntValOrVar(interp, expr->expr);
if (objPtr) {
- Jim_IncrRefCount(objPtr);
- *exprResultPtrPtr = objPtr;
+ Jim_SetResult(interp, objPtr);
return JIM_OK;
}
break;
case 2:
- if (expr->token[1].type == JIM_EXPROP_NOT) {
- objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
+ if (expr->expr->type == JIM_EXPROP_NOT) {
+ objPtr = JimExprIntValOrVar(interp, expr->expr->left);
if (objPtr && JimIsWide(objPtr)) {
- *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj;
- Jim_IncrRefCount(*exprResultPtrPtr);
+ Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
return JIM_OK;
}
}
break;
case 3:
- objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
+ objPtr = JimExprIntValOrVar(interp, expr->expr->left);
if (objPtr && JimIsWide(objPtr)) {
- Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]);
+ Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
if (objPtr2 && JimIsWide(objPtr2)) {
jim_wide wideValueA = JimWideValue(objPtr);
jim_wide wideValueB = JimWideValue(objPtr2);
int cmpRes;
- switch (expr->token[2].type) {
+ switch (expr->expr->type) {
case JIM_EXPROP_LT:
cmpRes = wideValueA < wideValueB;
break;
@@ -9497,8 +9375,7 @@ int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprRe
default:
goto noopt;
}
- *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj;
- Jim_IncrRefCount(*exprResultPtrPtr);
+ Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
return JIM_OK;
}
}
@@ -9508,131 +9385,39 @@ int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprRe
noopt:
#endif
- /* In order to avoid that the internal repr gets freed due to
+ /* In order to avoid the internal repr being freed due to
* shimmering of the exprObjPtr's object, we make the internal rep
* shared. */
expr->inUse++;
- /* The stack-based expr VM itself */
-
- /* Stack allocation. Expr programs have the feature that
- * a program of length N can't require a stack longer than
- * N. */
- if (expr->len > JIM_EE_STATICSTACK_LEN)
- e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len);
- else
- e.stack = staticStack;
-
- e.stacklen = 0;
-
- /* Execute every instruction */
- for (i = 0; i < expr->len && retcode == JIM_OK; i++) {
- Jim_Obj *objPtr;
-
- switch (expr->token[i].type) {
- case JIM_TT_EXPR_INT:
- case JIM_TT_EXPR_DOUBLE:
- case JIM_TT_EXPR_BOOLEAN:
- case JIM_TT_STR:
- ExprPush(&e, expr->token[i].objPtr);
- break;
-
- case JIM_TT_VAR:
- objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG);
- if (objPtr) {
- ExprPush(&e, objPtr);
- }
- else {
- retcode = JIM_ERR;
- }
- break;
-
- case JIM_TT_DICTSUGAR:
- objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr);
- if (objPtr) {
- ExprPush(&e, objPtr);
- }
- else {
- retcode = JIM_ERR;
- }
- break;
-
- case JIM_TT_ESC:
- retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE);
- if (retcode == JIM_OK) {
- ExprPush(&e, objPtr);
- }
- break;
-
- case JIM_TT_CMD:
- retcode = Jim_EvalObj(interp, expr->token[i].objPtr);
- if (retcode == JIM_OK) {
- ExprPush(&e, Jim_GetResult(interp));
- }
- break;
-
- default:{
- /* Find and execute the operation */
- e.skip = 0;
- e.opcode = expr->token[i].type;
-
- retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e);
- /* Skip some opcodes if necessary */
- i += e.skip;
- continue;
- }
- }
- }
+ /* Evaluate with the recursive expr engine */
+ retcode = JimExprEvalTermNode(interp, expr->expr);
expr->inUse--;
- if (retcode == JIM_OK) {
- *exprResultPtrPtr = ExprPop(&e);
- }
- else {
- for (i = 0; i < e.stacklen; i++) {
- Jim_DecrRefCount(interp, e.stack[i]);
- }
- }
- if (e.stack != staticStack) {
- Jim_Free(e.stack);
- }
return retcode;
}
int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
{
- int retcode;
- jim_wide wideValue;
- double doubleValue;
- int booleanValue;
- Jim_Obj *exprResultPtr;
+ int retcode = Jim_EvalExpression(interp, exprObjPtr);
- retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
- if (retcode != JIM_OK)
- return retcode;
+ if (retcode == JIM_OK) {
+ switch (ExprBool(interp, Jim_GetResult(interp))) {
+ case 0:
+ *boolPtr = 0;
+ break;
- if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
- if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) {
- if (Jim_GetBoolean(interp, exprResultPtr, &booleanValue) != JIM_OK) {
- Jim_DecrRefCount(interp, exprResultPtr);
- return JIM_ERR;
- } else {
- Jim_DecrRefCount(interp, exprResultPtr);
- *boolPtr = booleanValue;
- return JIM_OK;
- }
- }
- else {
- Jim_DecrRefCount(interp, exprResultPtr);
- *boolPtr = doubleValue != 0;
- return JIM_OK;
+ case 1:
+ *boolPtr = 1;
+ break;
+
+ case -1:
+ retcode = JIM_ERR;
+ break;
}
}
- *boolPtr = wideValue != 0;
-
- Jim_DecrRefCount(interp, exprResultPtr);
- return JIM_OK;
+ return retcode;
}
/* -----------------------------------------------------------------------------
@@ -9651,8 +9436,8 @@ int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
typedef struct ScanFmtPartDescr
{
- char *arg; /* Specification of a CHARSET conversion */
- char *prefix; /* Prefix to be scanned literally before conversion */
+ const char *arg; /* Specification of a CHARSET conversion */
+ const char *prefix; /* Prefix to be scanned literally before conversion */
size_t width; /* Maximal width of input to be converted */
int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */
char type; /* Type of conversion (e.g. c, d, f) */
@@ -9736,8 +9521,8 @@ static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
ScanFmtStringObj *fmtObj;
char *buffer;
int maxCount, i, approxSize, lastPos = -1;
- const char *fmt = objPtr->bytes;
- int maxFmtLen = objPtr->length;
+ const char *fmt = Jim_String(objPtr);
+ int maxFmtLen = Jim_Length(objPtr);
const char *fmtEnd = fmt + maxFmtLen;
int curr;
@@ -9822,6 +9607,11 @@ static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
return JIM_ERR;
}
}
+ if (descr->pos < 0) {
+ fmtObj->error =
+ "\"%n$\" conversion specifier is negative";
+ return JIM_ERR;
+ }
/* Try to find a width after the XPG3 specifier */
if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
descr->width = width;
@@ -9873,9 +9663,14 @@ static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
}
else {
/* Remember any valid modifier if given */
- if (strchr("hlL", *fmt) != 0)
+ if (fmt < fmtEnd && strchr("hlL", *fmt))
descr->modifier = tolower((int)*fmt++);
+ if (fmt >= fmtEnd) {
+ fmtObj->error = "missing scan conversion character";
+ return JIM_ERR;
+ }
+
descr->type = *fmt;
if (strchr("efgcsndoxui", *fmt) == 0) {
fmtObj->error = "bad scan conversion character";
@@ -10324,6 +10119,7 @@ static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
int retcode;
Jim_Cmd *cmdPtr;
+ void *prevPrivData;
#if 0
printf("invoke");
@@ -10353,6 +10149,7 @@ static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
goto out;
}
interp->evalDepth++;
+ prevPrivData = interp->cmdPrivData;
/* Call it -- Make sure result is an empty object. */
Jim_SetEmptyResult(interp);
@@ -10363,6 +10160,7 @@ static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
interp->cmdPrivData = cmdPtr->u.native.privData;
retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
}
+ interp->cmdPrivData = prevPrivData;
interp->evalDepth--;
out:
@@ -10450,6 +10248,7 @@ static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
{
Jim_Obj *objPtr;
+ int ret = JIM_ERR;
switch (token->type) {
case JIM_TT_STR:
@@ -10463,22 +10262,21 @@ static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Ob
objPtr = JimExpandDictSugar(interp, token->objPtr);
break;
case JIM_TT_EXPRSUGAR:
- objPtr = JimExpandExprSugar(interp, token->objPtr);
+ ret = Jim_EvalExpression(interp, token->objPtr);
+ if (ret == JIM_OK) {
+ objPtr = Jim_GetResult(interp);
+ }
+ else {
+ objPtr = NULL;
+ }
break;
case JIM_TT_CMD:
- switch (Jim_EvalObj(interp, token->objPtr)) {
- case JIM_OK:
- case JIM_RETURN:
- objPtr = interp->result;
- break;
- case JIM_BREAK:
- /* Stop substituting */
- return JIM_BREAK;
- case JIM_CONTINUE:
- /* just skip this one */
- return JIM_CONTINUE;
- default:
- return JIM_ERR;
+ ret = Jim_EvalObj(interp, token->objPtr);
+ if (ret == JIM_OK || ret == JIM_RETURN) {
+ objPtr = interp->result;
+ } else {
+ /* includes JIM_BREAK, JIM_CONTINUE */
+ objPtr = NULL;
}
break;
default:
@@ -10491,7 +10289,7 @@ static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Ob
*objPtrPtr = objPtr;
return JIM_OK;
}
- return JIM_ERR;
+ return ret;
}
/* Interpolate the given tokens into a unique Jim_Obj returned by reference
@@ -10549,7 +10347,8 @@ static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * tok
/* Fast path return for a single token */
if (tokens == 1 && intv[0] && intv == sintv) {
- Jim_DecrRefCount(interp, intv[0]);
+ /* Reverse the Jim_IncrRefCount() above, but don't free the object */
+ intv[0]->refCount--;
return intv[0];
}
@@ -10738,7 +10537,13 @@ int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
break;
case JIM_TT_EXPRSUGAR:
- wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr);
+ retcode = Jim_EvalExpression(interp, token[i].objPtr);
+ if (retcode == JIM_OK) {
+ wordObjPtr = Jim_GetResult(interp);
+ }
+ else {
+ wordObjPtr = NULL;
+ }
break;
case JIM_TT_DICTSUGAR:
wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
@@ -10922,7 +10727,6 @@ static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cm
}
}
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
- Jim_FreeNewObj(interp, argmsg);
}
#ifdef jim_ext_namespace
@@ -11060,7 +10864,8 @@ static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj
badargset:
- /* Free the callframe */
+ /* Invoke $jim::defer then destroy the callframe */
+ retcode = JimInvokeDefer(interp, retcode);
interp->framePtr = interp->framePtr->parent;
JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
@@ -11342,7 +11147,11 @@ static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
* resObjPtrPtr. */
int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
{
- ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);
+ ScriptObj *script;
+
+ JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object"));
+
+ script = Jim_GetSubst(interp, substObjPtr, flags);
Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */
/* In order to preserve the internal rep, we increment the
@@ -11371,16 +11180,14 @@ void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const
listObjPtr = Jim_NewListObj(interp, argv, argc);
- if (*msg) {
+ if (msg && *msg) {
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
}
Jim_IncrRefCount(listObjPtr);
objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
Jim_DecrRefCount(interp, listObjPtr);
- Jim_IncrRefCount(objPtr);
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
- Jim_DecrRefCount(interp, objPtr);
}
/**
@@ -11634,8 +11441,13 @@ static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, i
}
if (op == JIM_EXPROP_SUB)
res -= wideValue;
- else
+ else {
+ if (wideValue == 0) {
+ Jim_SetResultString(interp, "Division by zero", -1);
+ return JIM_ERR;
+ }
res /= wideValue;
+ }
}
Jim_SetResultInt(interp, res);
return JIM_OK;
@@ -11805,7 +11617,7 @@ static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
*/
if (retval == JIM_OK && boolean) {
ScriptObj *incrScript;
- ExprByteCode *expr;
+ struct ExprTree *expr;
jim_wide stop, currentVal;
Jim_Obj *objPtr;
int cmpOffset;
@@ -11819,47 +11631,53 @@ static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
goto evalstart;
}
/* Ensure proper token types. */
- if (incrScript->token[1].type != JIM_TT_ESC ||
- expr->token[0].type != JIM_TT_VAR ||
- (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) {
+ if (incrScript->token[1].type != JIM_TT_ESC) {
goto evalstart;
}
- if (expr->token[2].type == JIM_EXPROP_LT) {
+ if (expr->expr->type == JIM_EXPROP_LT) {
cmpOffset = 0;
}
- else if (expr->token[2].type == JIM_EXPROP_LTE) {
+ else if (expr->expr->type == JIM_EXPROP_LTE) {
cmpOffset = 1;
}
else {
goto evalstart;
}
+ if (expr->expr->left->type != JIM_TT_VAR) {
+ goto evalstart;
+ }
+
+ if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
+ goto evalstart;
+ }
+
/* Update command must be incr */
if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
goto evalstart;
}
/* incr, expression must be about the same variable */
- if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) {
+ if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
goto evalstart;
}
/* Get the stop condition (must be a variable or integer) */
- if (expr->token[1].type == JIM_TT_EXPR_INT) {
- if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) {
+ if (expr->expr->right->type == JIM_TT_EXPR_INT) {
+ if (Jim_GetWide(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
goto evalstart;
}
}
else {
- stopVarNamePtr = expr->token[1].objPtr;
+ stopVarNamePtr = expr->expr->right->objPtr;
Jim_IncrRefCount(stopVarNamePtr);
/* Keep the compiler happy */
stop = 0;
}
/* Initialization */
- varNamePtr = expr->token[0].objPtr;
+ varNamePtr = expr->expr->left->objPtr;
Jim_IncrRefCount(varNamePtr);
objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
@@ -12080,7 +11898,7 @@ static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *arg
}
if (result != JIM_OK) {
Jim_SetResultString(interp, "foreach varlist is empty", -1);
- return result;
+ goto empty_varlist;
}
if (doMap) {
@@ -12143,6 +11961,7 @@ static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *arg
Jim_SetResult(interp, resultObj);
err:
Jim_DecrRefCount(interp, resultObj);
+ empty_varlist:
if (numargs > 2) {
Jim_Free(iters);
}
@@ -12271,15 +12090,13 @@ int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patter
return eq;
}
-enum
-{ SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
-
/* [switch] */
static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
+ enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
- Jim_Obj *command = 0, *const *caseList = 0, *strObj;
- Jim_Obj *script = 0;
+ Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
+ Jim_Obj **caseList;
if (argc < 3) {
wrongnumargs:
@@ -12320,16 +12137,13 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
strObj = argv[opt++];
patCount = argc - opt;
if (patCount == 1) {
- Jim_Obj **vector;
-
- JimListGetElements(interp, argv[opt], &patCount, &vector);
- caseList = vector;
+ JimListGetElements(interp, argv[opt], &patCount, &caseList);
}
else
- caseList = &argv[opt];
+ caseList = (Jim_Obj **)&argv[opt];
if (patCount == 0 || patCount % 2 != 0)
goto wrongnumargs;
- for (i = 0; script == 0 && i < patCount; i += 2) {
+ for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
Jim_Obj *patObj = caseList[i];
if (!Jim_CompareStringImmediate(interp, patObj, "default")
@@ -12337,11 +12151,11 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
switch (matchOpt) {
case SWITCH_EXACT:
if (Jim_StringEqObj(strObj, patObj))
- script = caseList[i + 1];
+ scriptObj = caseList[i + 1];
break;
case SWITCH_GLOB:
if (Jim_StringMatchObj(interp, patObj, strObj, 0))
- script = caseList[i + 1];
+ scriptObj = caseList[i + 1];
break;
case SWITCH_RE:
command = Jim_NewStringObj(interp, "regexp", -1);
@@ -12353,34 +12167,31 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
* make sure to reconvert the object into a list
* again. Only for the single-list style [switch]. */
if (argc - opt == 1) {
- Jim_Obj **vector;
-
- JimListGetElements(interp, argv[opt], &patCount, &vector);
- caseList = vector;
+ JimListGetElements(interp, argv[opt], &patCount, &caseList);
}
/* command is here already decref'd */
if (rc < 0) {
return -rc;
}
if (rc)
- script = caseList[i + 1];
+ scriptObj = caseList[i + 1];
break;
}
}
}
else {
- script = caseList[i + 1];
+ scriptObj = caseList[i + 1];
}
}
- for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2)
- script = caseList[i + 1];
- if (script && Jim_CompareStringImmediate(interp, script, "-")) {
+ for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
+ scriptObj = caseList[i + 1];
+ if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
return JIM_ERR;
}
Jim_SetEmptyResult(interp);
- if (script) {
- return Jim_EvalObj(interp, script);
+ if (scriptObj) {
+ return Jim_EvalObj(interp, scriptObj);
}
return JIM_OK;
}
@@ -12684,18 +12495,11 @@ static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const
* <elements before first> <supplied elements> <elements after last>
*/
- /* Check to see if trying to replace past the end of the list */
- if (first < len) {
- /* OK. Not past the end */
- }
- else if (len == 0) {
- /* Special for empty list, adjust first to 0 */
- first = 0;
- }
- else {
- Jim_SetResultString(interp, "list doesn't contain element ", -1);
- Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]);
- return JIM_ERR;
+ /* Trying to replace past the end of the list means end of list
+ * See TIP #505
+ */
+ if (first > len) {
+ first = len;
}
/* Add the first set of elements */
@@ -12739,6 +12543,7 @@ static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const arg
Jim_Obj *resObj;
int i;
int retCode;
+ int shared;
struct lsort_info info;
@@ -12804,12 +12609,14 @@ static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const arg
break;
}
}
- resObj = Jim_DuplicateObj(interp, argv[argc - 1]);
+ resObj = argv[argc - 1];
+ if ((shared = Jim_IsShared(resObj)))
+ resObj = Jim_DuplicateObj(interp, resObj);
retCode = ListSortElements(interp, resObj, &info);
if (retCode == JIM_OK) {
Jim_SetResult(interp, resObj);
}
- else {
+ else if (shared) {
Jim_FreeNewObj(interp, resObj);
}
return retCode;
@@ -12856,6 +12663,33 @@ static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
return JIM_OK;
}
+#if defined(JIM_DEBUG_COMMAND) && !defined(JIM_BOOTSTRAP)
+/**
+ * Returns a zero-refcount list describing the expression at 'node'
+ */
+static Jim_Obj *JimGetExprAsList(Jim_Interp *interp, struct JimExprNode *node)
+{
+ Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
+
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, jim_tt_name(node->type), -1));
+ if (TOKEN_IS_EXPR_OP(node->type)) {
+ if (node->left) {
+ Jim_ListAppendElement(interp, listObjPtr, JimGetExprAsList(interp, node->left));
+ }
+ if (node->right) {
+ Jim_ListAppendElement(interp, listObjPtr, JimGetExprAsList(interp, node->right));
+ }
+ if (node->ternary) {
+ Jim_ListAppendElement(interp, listObjPtr, JimGetExprAsList(interp, node->ternary));
+ }
+ }
+ else {
+ Jim_ListAppendElement(interp, listObjPtr, node->objPtr);
+ }
+ return listObjPtr;
+}
+#endif /* JIM_DEBUG_COMMAND && !JIM_BOOTSTRAP */
+
/* [debug] */
static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
@@ -12877,7 +12711,7 @@ static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
return JIM_ERR;
}
if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK)
- return JIM_ERR;
+ return Jim_CheckShowCommands(interp, argv[1], options);
if (option == OPT_REFCOUNT) {
if (argc != 3) {
Jim_WrongNumArgs(interp, 2, argv, "object");
@@ -12984,7 +12818,7 @@ static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
return JIM_OK;
}
else if (option == OPT_EXPRLEN) {
- ExprByteCode *expr;
+ struct ExprTree *expr;
if (argc != 3) {
Jim_WrongNumArgs(interp, 2, argv, "expression");
@@ -12997,9 +12831,7 @@ static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
return JIM_OK;
}
else if (option == OPT_EXPRBC) {
- Jim_Obj *objPtr;
- ExprByteCode *expr;
- int i;
+ struct ExprTree *expr;
if (argc != 3) {
Jim_WrongNumArgs(interp, 2, argv, "expression");
@@ -13008,55 +12840,7 @@ static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
expr = JimGetExpression(interp, argv[2]);
if (expr == NULL)
return JIM_ERR;
- objPtr = Jim_NewListObj(interp, NULL, 0);
- for (i = 0; i < expr->len; i++) {
- const char *type;
- const Jim_ExprOperator *op;
- Jim_Obj *obj = expr->token[i].objPtr;
-
- switch (expr->token[i].type) {
- case JIM_TT_EXPR_INT:
- type = "int";
- break;
- case JIM_TT_EXPR_DOUBLE:
- type = "double";
- break;
- case JIM_TT_EXPR_BOOLEAN:
- type = "boolean";
- break;
- case JIM_TT_CMD:
- type = "command";
- break;
- case JIM_TT_VAR:
- type = "variable";
- break;
- case JIM_TT_DICTSUGAR:
- type = "dictsugar";
- break;
- case JIM_TT_EXPRSUGAR:
- type = "exprsugar";
- break;
- case JIM_TT_ESC:
- type = "subst";
- break;
- case JIM_TT_STR:
- type = "string";
- break;
- default:
- op = JimExprOperatorInfoByOpcode(expr->token[i].type);
- if (op == NULL) {
- type = "private";
- }
- else {
- type = "operator";
- }
- obj = Jim_NewStringObj(interp, op ? op->name : "", -1);
- break;
- }
- Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, type, -1));
- Jim_ListAppendElement(interp, objPtr, obj);
- }
- Jim_SetResult(interp, objPtr);
+ Jim_SetResult(interp, JimGetExprAsList(interp, expr->expr));
return JIM_OK;
}
else {
@@ -13144,18 +12928,17 @@ static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
/* [expr] */
static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- Jim_Obj *exprResultPtr;
int retcode;
if (argc == 2) {
- retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
+ retcode = Jim_EvalExpression(interp, argv[1]);
}
else if (argc > 2) {
Jim_Obj *objPtr;
objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
Jim_IncrRefCount(objPtr);
- retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
+ retcode = Jim_EvalExpression(interp, objPtr);
Jim_DecrRefCount(interp, objPtr);
}
else {
@@ -13164,8 +12947,6 @@ static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
}
if (retcode != JIM_OK)
return retcode;
- Jim_SetResult(interp, exprResultPtr);
- Jim_DecrRefCount(interp, exprResultPtr);
return JIM_OK;
}
@@ -13629,7 +13410,7 @@ static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
}
if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
- return JIM_ERR;
+ return Jim_CheckShowCommands(interp, argv[1], options);
switch (option) {
case OPT_LENGTH:
@@ -14269,55 +14050,43 @@ static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
}
-#define JIM_DICTMATCH_VALUES 0x0001
-
-typedef void JimDictMatchCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type);
-
-static void JimDictMatchKeys(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type)
-{
- Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
- if (type & JIM_DICTMATCH_VALUES) {
- Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
- }
-}
+#define JIM_DICTMATCH_KEYS 0x0001
+#define JIM_DICTMATCH_VALUES 0x002
/**
- * Like JimHashtablePatternMatch, but for dictionaries.
+ * match_type must be one of JIM_DICTMATCH_KEYS or JIM_DICTMATCH_VALUES
+ * return_types should be either or both
*/
-static Jim_Obj *JimDictPatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
- JimDictMatchCallbackType *callback, int type)
+int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
{
Jim_HashEntry *he;
- Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
-
- /* Check for the non-pattern case. We can do this much more efficiently. */
+ Jim_Obj *listObjPtr;
Jim_HashTableIterator htiter;
- JimInitHashTableIterator(ht, &htiter);
- while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
- if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) {
- callback(interp, listObjPtr, he, type);
- }
- }
-
- return listObjPtr;
-}
-
-int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
-{
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return JIM_ERR;
}
- Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, 0));
- return JIM_OK;
-}
-int Jim_DictValues(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
-{
- if (SetDictFromAny(interp, objPtr) != JIM_OK) {
- return JIM_ERR;
+ listObjPtr = Jim_NewListObj(interp, NULL, 0);
+
+ JimInitHashTableIterator(objPtr->internalRep.ptr, &htiter);
+ while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
+ if (patternObj) {
+ Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? (Jim_Obj *)he->key : Jim_GetHashEntryVal(he);
+ if (!JimGlobMatch(Jim_String(patternObj), Jim_String(matchObj), 0)) {
+ /* no match */
+ continue;
+ }
+ }
+ if (return_types & JIM_DICTMATCH_KEYS) {
+ Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
+ }
+ if (return_types & JIM_DICTMATCH_VALUES) {
+ Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
+ }
}
- Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, JIM_DICTMATCH_VALUES));
+
+ Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
@@ -14329,10 +14098,46 @@ int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
}
+/**
+ * Must be called with at least one object.
+ * Returns the new dictionary, or NULL on error.
+ */
+Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
+{
+ Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
+ int i;
+
+ JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
+
+ /* Note that we don't optimise the trivial case of a single argument */
+
+ for (i = 0; i < objc; i++) {
+ Jim_HashTable *ht;
+ Jim_HashTableIterator htiter;
+ Jim_HashEntry *he;
+
+ if (SetDictFromAny(interp, objv[i]) != JIM_OK) {
+ Jim_FreeNewObj(interp, objPtr);
+ return NULL;
+ }
+ ht = objv[i]->internalRep.ptr;
+ JimInitHashTableIterator(ht, &htiter);
+ while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
+ Jim_ReplaceHashEntry(objPtr->internalRep.ptr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he));
+ }
+ }
+ return objPtr;
+}
+
int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_HashTable *ht;
unsigned int i;
+ char buffer[100];
+ int sum = 0;
+ int nonzero_count = 0;
+ Jim_Obj *output;
+ int bucket_counts[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return JIM_ERR;
@@ -14341,21 +14146,36 @@ int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
ht = (Jim_HashTable *)objPtr->internalRep.ptr;
/* Note that this uses internal knowledge of the hash table */
- printf("%d entries in table, %d buckets\n", ht->used, ht->size);
+ snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets\n", ht->used, ht->size);
+ output = Jim_NewStringObj(interp, buffer, -1);
for (i = 0; i < ht->size; i++) {
Jim_HashEntry *he = ht->table[i];
-
- if (he) {
- printf("%d: ", i);
-
- while (he) {
- printf(" %s", Jim_String(he->key));
- he = he->next;
- }
- printf("\n");
+ int entries = 0;
+ while (he) {
+ entries++;
+ he = he->next;
+ }
+ if (entries > 9) {
+ bucket_counts[10]++;
+ }
+ else {
+ bucket_counts[entries]++;
+ }
+ if (entries) {
+ sum += entries;
+ nonzero_count++;
}
}
+ for (i = 0; i < 10; i++) {
+ snprintf(buffer, sizeof(buffer), "number of buckets with %d entries: %d\n", i, bucket_counts[i]);
+ Jim_AppendString(interp, output, buffer, -1);
+ }
+ snprintf(buffer, sizeof(buffer), "number of buckets with 10 or more entries: %d\n", bucket_counts[10]);
+ Jim_AppendString(interp, output, buffer, -1);
+ snprintf(buffer, sizeof(buffer), "average search distance for entry: %.1f", nonzero_count ? (double)sum / nonzero_count : 0.0);
+ Jim_AppendString(interp, output, buffer, -1);
+ Jim_SetResult(interp, output);
return JIM_OK;
}
@@ -14369,10 +14189,66 @@ static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char
return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
}
+/**
+ * Implements the [dict with] command
+ */
+static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
+{
+ int i;
+ Jim_Obj *objPtr;
+ Jim_Obj *dictObj;
+ Jim_Obj **dictValues;
+ int len;
+ int ret = JIM_OK;
+
+ /* Open up the appropriate level of the dictionary */
+ dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
+ if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
+ return JIM_ERR;
+ }
+ /* Set the local variables */
+ if (Jim_DictPairs(interp, objPtr, &dictValues, &len) == JIM_ERR) {
+ return JIM_ERR;
+ }
+ for (i = 0; i < len; i += 2) {
+ if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
+ Jim_Free(dictValues);
+ return JIM_ERR;
+ }
+ }
+
+ /* As an optimisation, if the script is empty, no need to evaluate it or update the dict */
+ if (Jim_Length(scriptObj)) {
+ ret = Jim_EvalObj(interp, scriptObj);
+
+ /* Now if the dictionary still exists, update it based on the local variables */
+ if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
+ /* We need a copy of keyv with one extra element at the end for Jim_SetDictKeysVector() */
+ Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
+ for (i = 0; i < keyc; i++) {
+ newkeyv[i] = keyv[i];
+ }
+
+ for (i = 0; i < len; i += 2) {
+ /* This will be NULL if the variable no longer exists, thus deleting the variable */
+ objPtr = Jim_GetVariable(interp, dictValues[i], 0);
+ newkeyv[keyc] = dictValues[i];
+ Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, 0);
+ }
+ Jim_Free(newkeyv);
+ }
+ }
+
+ Jim_Free(dictValues);
+
+ return ret;
+}
+
/* [dict] */
static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
+ int types = JIM_DICTMATCH_KEYS;
int option;
static const char * const options[] = {
"create", "get", "set", "unset", "exists", "keys", "size", "info",
@@ -14392,7 +14268,7 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
}
if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
- return JIM_ERR;
+ return Jim_CheckShowCommands(interp, argv[1], options);
}
switch (option) {
@@ -14439,12 +14315,15 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
}
return JIM_OK;
+ case OPT_VALUES:
+ types = JIM_DICTMATCH_VALUES;
+ /* fallthru */
case OPT_KEYS:
if (argc != 3 && argc != 4) {
Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?");
return JIM_ERR;
}
- return Jim_DictKeys(interp, argv[2], argc == 4 ? argv[3] : NULL);
+ return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
case OPT_SIZE:
if (argc != 3) {
@@ -14461,11 +14340,12 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
if (argc == 2) {
return JIM_OK;
}
- if (Jim_DictSize(interp, argv[2]) < 0) {
+ objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
+ if (objPtr == NULL) {
return JIM_ERR;
}
- /* Handle as ensemble */
- break;
+ Jim_SetResult(interp, objPtr);
+ return JIM_OK;
case OPT_UPDATE:
if (argc < 6 || argc % 2) {
@@ -14489,6 +14369,13 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
return JIM_ERR;
}
return Jim_DictInfo(interp, argv[2]);
+
+ case OPT_WITH:
+ if (argc < 4) {
+ Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script");
+ return JIM_ERR;
+ }
+ return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
}
/* Handle command as an ensemble */
return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2);
@@ -14571,9 +14458,8 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
return JIM_ERR;
}
- if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV)
- != JIM_OK) {
- return JIM_ERR;
+ if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
+ return Jim_CheckShowCommands(interp, argv[1], commands);
}
/* Test for the most common commands first, just in case it makes a difference */
@@ -15448,36 +15334,87 @@ void Jim_MakeErrorMessage(Jim_Interp *interp)
Jim_EvalObjVector(interp, 2, argv);
}
-static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
- const char *prefix, const char *const *tablePtr, const char *name)
+/*
+ * Given a null terminated array of strings, returns an allocated, sorted
+ * copy of the array.
+ */
+static char **JimSortStringTable(const char *const *tablePtr)
{
int count;
char **tablePtrSorted;
- int i;
+ /* Find the size of the table */
for (count = 0; tablePtr[count]; count++) {
}
+ /* Allocate one extra for the terminating NULL pointer */
+ tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
+ memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
+ qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
+ tablePtrSorted[count] = NULL;
+
+ return tablePtrSorted;
+}
+
+static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
+ const char *prefix, const char *const *tablePtr, const char *name)
+{
+ char **tablePtrSorted;
+ int i;
+
if (name == NULL) {
name = "option";
}
Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
- tablePtrSorted = Jim_Alloc(sizeof(char *) * count);
- memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
- qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
- for (i = 0; i < count; i++) {
- if (i + 1 == count && count > 1) {
+ tablePtrSorted = JimSortStringTable(tablePtr);
+ for (i = 0; tablePtrSorted[i]; i++) {
+ if (tablePtrSorted[i + 1] == NULL && i > 0) {
Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
}
Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
- if (i + 1 != count) {
+ if (tablePtrSorted[i + 1]) {
Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
}
}
Jim_Free(tablePtrSorted);
}
+
+/*
+ * If objPtr is "-commands" sets the Jim result as a sorted list of options in the table
+ * and returns JIM_OK.
+ *
+ * Otherwise returns JIM_ERR.
+ */
+int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
+{
+ if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
+ int i;
+ char **tablePtrSorted = JimSortStringTable(tablePtr);
+ Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
+ for (i = 0; tablePtrSorted[i]; i++) {
+ Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
+ }
+ Jim_Free(tablePtrSorted);
+ return JIM_OK;
+ }
+ return JIM_ERR;
+}
+
+/* internal rep is stored in ptrIntvalue
+ * ptr = tablePtr
+ * int1 = flags
+ * int2 = index
+ */
+static const Jim_ObjType getEnumObjType = {
+ "get-enum",
+ NULL,
+ NULL,
+ NULL,
+ JIM_TYPE_REFERENCES
+};
+
int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
const char *const *tablePtr, int *indexPtr, const char *name, int flags)
{
@@ -15486,15 +15423,24 @@ int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
int i;
int match = -1;
int arglen;
- const char *arg = Jim_GetString(objPtr, &arglen);
+ const char *arg;
+
+ if (objPtr->typePtr == &getEnumObjType) {
+ if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
+ *indexPtr = objPtr->internalRep.ptrIntValue.int2;
+ return JIM_OK;
+ }
+ }
+
+ arg = Jim_GetString(objPtr, &arglen);
*indexPtr = -1;
for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
/* Found an exact match */
- *indexPtr = i;
- return JIM_OK;
+ match = i;
+ goto found;
}
if (flags & JIM_ENUM_ABBREV) {
/* Accept an unambiguous abbreviation.
@@ -15515,6 +15461,14 @@ int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
/* If we had an unambiguous partial match */
if (match >= 0) {
+ found:
+ /* Record the match in the object */
+ Jim_FreeIntRep(interp, objPtr);
+ objPtr->typePtr = &getEnumObjType;
+ objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
+ objPtr->internalRep.ptrIntValue.int1 = flags;
+ objPtr->internalRep.ptrIntValue.int2 = match;
+ /* Return the result */
*indexPtr = match;
return JIM_OK;
}
@@ -15561,6 +15515,8 @@ int Jim_IsList(Jim_Obj *objPtr)
* e.g. Jim_SetResultFormatted(interp, "Bad option \"%#s\" in proc \"%#s\"", optionObjPtr, procNamePtr);
*
* Note: We take advantage of the fact that printf has the same behaviour for both %s and %#s
+ *
+ * Note that any Jim_Obj parameters with zero ref count will be freed as a result of this call.
*/
void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
{
@@ -15569,6 +15525,8 @@ void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
int extra = 0;
int n = 0;
const char *params[5];
+ int nobjparam = 0;
+ Jim_Obj *objparam[5];
char *buf;
va_list args;
int i;
@@ -15587,6 +15545,8 @@ void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
params[n] = Jim_GetString(objPtr, &l);
+ objparam[nobjparam++] = objPtr;
+ Jim_IncrRefCount(objPtr);
}
else {
if (format[i] == '%') {
@@ -15605,6 +15565,10 @@ void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
va_end(args);
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
+
+ for (i = 0; i < nobjparam; i++) {
+ Jim_DecrRefCount(interp, objparam[i]);
+ }
}
/* stubs */