diff --git a/GNUmakefile b/GNUmakefile
index bd9bb5f7e0268f3bfd08b605be972c508b773932..d56841bd83d13cb70f425480c5009718dfc2ca12 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -10,6 +10,8 @@ SOURCES += require.c
 DBDS    += require.dbd
 SOURCES += runScript.c
 DBDS    += runScript.dbd
+SOURCES += expr.c
+#DBDS    += expr.dbd
 
 SOURCES += dbLoadTemplate.y
 DBDS    += dbLoadTemplate.dbd
diff --git a/Makefile b/Makefile
index ea3af0d84193dd346d987bebe498558475d88a66..2f85c3b20e84a1b3d5a5a9b6968c2885eae64037 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,9 @@ requireSup_DBD += require.dbd
 LIB_SRCS += runScript.c
 requireSup_DBD += runScript.dbd
 
+LIB_SRCS += expr.c
+#requireSup_DBD += expr.dbd
+
 LIB_SRCS += dbLoadTemplate.y
 requireSup_DBD += dbLoadTemplate.dbd
 
diff --git a/expr.c b/expr.c
new file mode 100644
index 0000000000000000000000000000000000000000..a3eee063fd1b8163bc909838af5fc9aea11088dc
--- /dev/null
+++ b/expr.c
@@ -0,0 +1,260 @@
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "expr.h"
+
+int exprDebug;
+
+static int parseExpr(const char** pp, long* v, int op);
+
+static int parseValue(const char** pp, long* v)
+{
+    long val;
+    const char *p = *pp;
+    char o;
+
+    /* A value is optionally prefixed with an unary operator + - ! ~.
+     * It is either a number (decimal, octal or hex)
+     * or an expression in ().
+     * Allowed chars after a number: operators, closing parenthesis, whitespace, quotes, end of string
+     */
+
+    /* first look for value */
+    while (isspace((unsigned char)*p)) p++;
+    o = *p;
+    if (memchr("+-~!", o, 4))
+    {
+        /* handle unary operators */
+        p++;
+        if (!parseValue(&p, &val)) return 0; /* no valid value */
+        if (o == '-') val=-val;
+        else if (o == '~') val=~val;
+        else if (o == '!') val=!val;
+    }
+    else if (o == '(')
+    {
+        /*  handle sub-expression */
+        p++;
+        if (parseExpr(&p, &val, 0) < 0) return 0; /* no valid expression */
+        while (isspace((unsigned char)*p)) p++;
+        if (*p++ != ')') return 0; /* missing ) */
+    }
+    else
+    {
+        /* get number */
+        char* e;
+        val = strtol(p, &e, 0);
+        if (e == p) return 0; /* no number */
+        
+        if (isalpha((unsigned char)*p))
+        {
+            /* followed by rubbish */
+            return 0; 
+        }
+        p = e;
+    }
+    *pp = p;
+    *v = val;
+    return 1;
+}
+
+static long ipow(long base, long exp)
+{
+    long v;
+    if (exp < 0) return 0;
+    if (exp == 0) return 1;
+    v = base;
+    while (--exp) v *= base; /* optimize this! */
+    return v;
+}
+
+struct {char str[4]; int pr;} ops[] = {
+    {"<-",0},
+    {"**",11},
+    {"*", 10},{"/",10},{"%",10},
+    {"+",9},{"-",9},
+    {"<<",8},{">>>",8},{">>",8},
+    {"<=>",7},{"<=",7},{">=",7},{"<",7},{">",7},
+    {"==",6},{"!=",6},
+    {"&&",5},{"||",5},
+    {"&",4},{"^",3},{"|",2},{"?",1}
+};
+
+static int startsWith(const char* p, const char* s)
+{
+    int i = 0;
+    while (*s) { i++; if (*p++ != *s++) return 0; }
+    return i;
+}
+
+static int parseOp(const char** pp)
+{
+    const char *p = *pp;
+    int o, l;
+
+    while (isspace((unsigned char)*p)) p++;
+    if (ispunct((unsigned char)*p))
+    {
+        for (o = 1; o < (int)(sizeof(ops)/sizeof(ops[0])); o++)
+        {
+            if ((l = startsWith(p, ops[o].str)))
+            {
+                /* operator found */
+                *pp = p+l;
+                return o;
+            }
+        }
+    }
+    return 0;
+}
+
+static int parseExpr(const char** pp, long* v, int o)
+{
+    const char *p = *pp;
+    long val = *v;
+    long val2;
+    int o2 = o;
+    int pr = ops[o].pr;
+
+    if (exprDebug) printf("parseExpr(%s %d): start %ld %s %s\n", ops[o].str, pr, val, ops[o].str, p);
+    do {
+        if (!parseValue(&p, &val2)) return -1;
+        if ((o2 = parseOp(&p)))
+        {
+            if (exprDebug) printf("parseExpr(%d): %ld %s %ld %s %s\n", pr, val, ops[o].str, val2, ops[o2].str, p);
+            if (o && ops[o2].pr > ops[o].pr)
+            {
+                if ((o2 = parseExpr(&p, &val2, o2)) < 0) return -1;
+            }
+        }
+        if (exprDebug) printf("parseExpr(%d): %ld %s %ld", pr, val, ops[o].str, val2);
+        switch(o)
+        {
+            case  0: val = val2; break;
+            case  1: val = ipow(val, val2); break;
+            case  2: val *= val2; break;
+            case  3: val /= val2; break;
+            case  4: val %= val2; break;
+            case  5: val += val2; break;
+            case  6: val -= val2; break;
+            case  7: val <<= val2; break;
+            case  8: val = (unsigned long)val >> val2; break;
+            case  9: val >>= val2; break;
+            case 10: val = val < val2 ? -1 : val == val2 ? 0 : 1; break;
+            case 11: val = val <= val2; break;
+            case 12: val = val >= val2; break;
+            case 13: val = val < val2; break;
+            case 14: val = val > val2; break;
+            case 15: val = val == val2; break;
+            case 16: val = val != val2; break;
+            case 17: val = val && val2; break;
+            case 18: val = val || val2; break;
+            case 19: val &= val2; break;
+            case 20: val ^= val2; break;
+            case 21: val |= val2; break;
+            case 22: val = (val != 0); break;
+        }
+        if (exprDebug) printf(" = %ld\n", val);
+        o = o2;
+    } while (o && pr <= ops[o].pr);
+    if (exprDebug) printf("parseExpr(%d): result %ld\n", pr, val);
+    *pp = p;
+    *v = val;
+    return o;
+}
+
+const char* getFormat(const char** pp)
+{
+    static char format [20];
+    const char* p = *pp;
+    unsigned int i = 1;
+    if (exprDebug) printf("getFormat %s\n", p);
+    if ((format[0] = *p++) == '%')
+    {
+        while (i < sizeof(format) && memchr(" #-+0", *p, 5))
+            format[i++] = *p++;
+        while (i < sizeof(format) && *p >= '0' && *p <= '9')
+            format[i++] = *p++;
+        if (i < sizeof(format))
+            format[i++] = 'l';
+        if (i < sizeof(format) && memchr("diouxXc", *p, 7))
+        {
+            format[i++] = *p++;
+            format[i] = 0;
+            *pp = p;
+            if (exprDebug) printf("format = '%s'\n", format);
+            return format;
+        }
+    }
+    if (exprDebug) printf("no format\n");
+    return NULL;
+}
+
+size_t replaceExpressions(const char* r, char* buffer, size_t buffersize)
+{
+    long val;
+    char* w = buffer;
+    char* s;
+
+    while (*r)
+    {
+        s = w;
+        if (*r == '"' || *r == '\'')
+        {
+            /* quoted strings */
+            char c = *w++ = *r++;
+            while (*r && *r != c) {
+                if (*r == '\\' && !(*w++ = *r++)) break;
+                *w++ = *r++;
+            }
+            if (*r) *w++ = *r++;
+            *w = 0;
+            if (exprDebug) printf("quoted string %s\n", s);
+        }
+        else if (*r == '%')
+        {
+            /* formatted expression */
+            const char* r2 = r;
+            const char* f;
+            if (exprDebug) printf("formatted expression after '%s'\n", s);
+            if ((f = getFormat(&r2)) && parseExpr(&r2, &val, 0) == 0)
+            {
+                r = r2;
+                if (*s == '(' && *r2++ == ')')
+                {
+                    w = s;
+                    r = r2;
+                }
+                w += sprintf(w, f , val);
+                if (exprDebug) printf("formatted expression %s\n", s);
+            }
+        }
+        else if (parseExpr(&r, &val, 0) == 0)
+        {
+            /* unformatted expression */
+            w += sprintf(w, "%ld", val);
+            *w = 0;
+            if (exprDebug) printf("simple expression %s\n", s);
+        }
+        else if (*r == ',')
+        {
+            /* single comma */
+            *w++ = *r++;
+        }
+        else {
+            /* unquoted string (i.e plain word) */
+            do {
+                *w++ = *r++;
+            } while (*r && !strchr("%(\"', \t\n", *r));
+            *w = 0;
+            if (exprDebug) printf("plain word '%s'\n", s);
+        }
+        /* copy space */
+        while (isspace((unsigned char)*r)) *w++ = *r++;
+        /* terminate */
+        *w = 0;
+    }
+    return w - buffer;
+}
diff --git a/expr.h b/expr.h
new file mode 100644
index 0000000000000000000000000000000000000000..d3ee4edaf5126b50accd9ed5a144625506589191
--- /dev/null
+++ b/expr.h
@@ -0,0 +1,21 @@
+#ifndef expr_h
+#define expr_h
+
+#ifdef __cplusplus
+extern {
+#endif
+
+extern int exprDebug;
+
+size_t replaceExpressions(const char* source, char* buffer, size_t buffersize);
+/* Resolve integer expressions that are either free standing
+ * or in parentheses () embedded in an unquoted word.
+ * Do not resolve expressions in single or double quoted strings.
+ * An expression optionally starts with a integer format such as %x.
+ * It consists of integer numbers, operators and parentheses ().
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/runScript.c b/runScript.c
index ebe0976e3b0cc276c6ab830fe31b23ccbbcd941c..daf79fa343ed257ac9be183d4447db286bf374a7 100644
--- a/runScript.c
+++ b/runScript.c
@@ -47,193 +47,11 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
 
 #define IS_ABS_PATH(filename) (filename[0] == OSI_PATH_SEPARATOR[0])  /* may be different for other OS ? */
 
+#include "expr.h"
 #include "require.h"
 
 int runScriptDebug=0;
 
-static int parseExpr(const char** pp, long* v, int op);
-
-static int parseValue(const char** pp, long* v)
-{
-    long val;
-    const char *p = *pp;
-    char o;
-
-    /* A value is optionally prefixed with an unary operator + - ! ~.
-     * It is either a number (decimal, octal or hex)
-     * or an expression in ().
-     * Allowed chars after a number: operators, closing parenthesis, whitespace, quotes, end of string
-     */
-
-    /* first look for value */
-    while (isspace((unsigned char)*p)) p++;
-    o = *p;
-    if (memchr("+-~!", o, 4))
-    {
-        /* handle unary operators */
-        p++;
-        if (!parseValue(&p, &val)) return 0; /* no valid value */
-        if (o == '-') val=-val;
-        else if (o == '~') val=~val;
-        else if (o == '!') val=!val;
-    }
-    else if (o == '(')
-    {
-        /*  handle sub-expression */
-        if (runScriptDebug > 1) printf("parseValue: subexpression '%s'\n", p);
-        p++;
-        if (parseExpr(&p, &val, 0) < 0) return 0; /* no valid expression */
-        while (isspace((unsigned char)*p)) p++;
-        if (*p++ != ')') return 0; /* missing ) */
-    }
-    else
-    {
-        /* get number */
-        char* e;
-        val = strtol(p, &e, 0);
-        if (e == p) return 0; /* no number */
-        
-        if (isalpha((unsigned char)*p))
-        {
-            /* followed by rubbish */
-            if (runScriptDebug > 1) printf("parseValue: bail out from '%s' at '%s'\n", *pp, e);
-            return 0; 
-        }
-        p = e;
-    }
-    if (runScriptDebug > 1) printf("parseValue: '%.*s' = %ld rest '%s'\n", (int)(p-*pp), *pp, val, p);
-    *pp = p;
-    *v = val;
-    return 1;
-}
-
-static long ipow(long base, long exp)
-{
-    long v;
-    if (exp < 0) return 0;
-    if (exp == 0) return 1;
-    v = base;
-    while (--exp) v *= base; /* optimize this! */
-    return v;
-}
-
-struct {char str[3]; char pr;} ops[] = {
-    {"",0},
-    {"**",2},
-    {"*", 3},{"/",3},{"%",3},
-    {"+",4},{"-",4},
-    {"<<",5},{">>>",5},{">>",5},
-    {"<=>",6},{"<=",6},{">=",6},{"<",6},{">",6},
-    {"==",7},{"!=",7},
-    {"&&",11},{"||",12},
-    {"&",8},{"^",9},{"|",10}
-};
-
-static int startsWith(const char* p, const char* s)
-{
-    int i = 0;
-    while (*s) { i++; if (*p++ != *s++) return 0; }
-    return i;
-}
-
-static int parseOp(const char** pp)
-{
-    const char *p = *pp;
-    int o, l;
-
-    while (isspace((unsigned char)*p)) p++;
-    if (runScriptDebug > 1) printf("parseOp: check %s\n", p);
-    if (ispunct((unsigned char)*p))
-    {
-        for (o = 1; o < (int)(sizeof(ops)/sizeof(ops[0])); o++)
-        {
-            if ((l = startsWith(p, ops[o].str)))
-            {
-                /* operator found */
-                if (runScriptDebug > 1) printf("parseOp: %.3s\n", ops[0].str);
-                *pp = p+l;
-                return o;
-            }
-        }
-    }
-    if (runScriptDebug > 1) printf("parseOp: end\n");
-    return 0;
-}
-
-static int parseExpr(const char** pp, long* v, int o)
-{
-    const char *p = *pp;
-    long val = 0;
-    long val2;
-    int o2 = o;
-    int pr = ops[o].pr;
-
-    if (runScriptDebug > 1) printf("parseExpr: o = %.3s\n", ops[o].str);
-    do {
-        if (!parseValue(&p, &val2)) return -1;
-        if ((o2 = parseOp(&p)) != 0 && ops[o2].pr < pr)
-            if ((o2 = parseExpr(&p, &val2, o2)) < 0) return -1;
-        if (runScriptDebug > 1) printf("parseExpr: %ld %.3s %ld\n", val, ops[o].str, val2);
-        switch(o)
-        {
-            case  0: val = val2; break;
-            case  1: val = ipow(val, val2); break;
-            case  2: val *= val2; break;
-            case  3: val /= val2; break;
-            case  4: val %= val2; break;
-            case  5: val += val2; break;
-            case  6: val -= val2; break;
-            case  7: val <<= val2; break;
-            case  8: val = (unsigned long)val >> val2; break;
-            case  9: val >>= val2; break;
-            case 10: val = val < val2 ? -1 : val == val2 ? 0 : 1; break;
-            case 11: val = val <= val2; break;
-            case 12: val = val >= val2; break;
-            case 13: val = val < val2; break;
-            case 14: val = val > val2; break;
-            case 15: val = val == val2; break;
-            case 16: val = val != val2; break;
-            case 17: val = val && val2; break;
-            case 18: val = val || val2; break;
-            case 19: val &= val2; break;
-            case 20: val ^= val2; break;
-            case 21: val |= val2; break;
-        }
-        o = o2;
-    } while (o && pr <= ops[o].pr);
-    if (runScriptDebug > 1) printf("parseExpr: result %ld\n", val);
-    *pp = p;
-    *v = val;
-    return o;
-}
-
-const char* getFormat(const char** pp)
-{
-    static char format [20];
-    const char* p = *pp;
-    unsigned int i = 1;
-    if (runScriptDebug > 1) printf("getFormat %s\n", p);
-    if ((format[0] = *p++) == '%')
-    {
-        while (i < sizeof(format) && memchr(" #-+0", *p, 5))
-            format[i++] = *p++;
-        while (i < sizeof(format) && *p >= '0' && *p <= '9')
-            format[i++] = *p++;
-        if (i < sizeof(format))
-            format[i++] = 'l';
-        if (i < sizeof(format) && memchr("diouxXc", *p, 7))
-        {
-            format[i++] = *p++;
-            format[i] = 0;
-            *pp = p;
-            if (runScriptDebug > 1) printf("format = '%s'\n", format);
-            return format;
-        }
-    }
-    if (runScriptDebug > 1) printf("no format\n");
-    return NULL;
-}
-
 int runScript(const char* filename, const char* args)
 {
     MAC_HANDLE *mac = NULL;
@@ -384,78 +202,8 @@ int runScript(const char* filename, const char* args)
         /* find local variable assignments */
         if ((x = strpbrk(p, "=(, \t\n\r")) != NULL && *x=='=')
         {
-            const char* r;
-            char* s;
-            char* w;
-            long val;
-
             *x++ = 0;
-            r = x;
-            w = line_raw;
-            while (*r)
-            {
-                /* Resolve integer expressions that are either free standing
-                 * or in parentheses () embedded in an unquoted word.
-                 * Do not resolve expressions in single or double quoted strings.
-                 * An expression optionally starts with a integer format such as %x.
-                 * It consists of integer numbers, operators and parentheses ().
-                 */
-                s = w;
-                if (*r == '"' || *r == '\'')
-                {
-                    /* quoted strings */
-                    char c = *w++ = *r++;
-                    while (*r && *r != c) {
-                        if (*r == '\\' && !(*w++ = *r++)) break;
-                        *w++ = *r++;
-                    }
-                    if (*r) *w++ = *r++;
-                    *w = 0;
-                    if (runScriptDebug > 1) printf("quoted string %s\n", s);
-                }
-                else if (*r == '%')
-                {
-                    /* formatted expression */
-                    const char* r2 = r;
-                    const char* f;
-                    if (runScriptDebug > 1) printf("formatted expression after '%s'\n", s);
-                    if ((f = getFormat(&r2)) && parseExpr(&r2, &val, 0) == 0)
-                    {
-                        r = r2;
-                        if (*s == '(' && *r2++ == ')')
-                        {
-                            w = s;
-                            r = r2;
-                        }
-                        w += sprintf(w, f , val);
-                        if (runScriptDebug > 1) printf("formatted expression %s\n", s);
-                    }
-                }
-                else if (parseExpr(&r, &val, 0) == 0)
-                {
-                    /* unformatted expression */
-                    w += sprintf(w, "%ld", val);
-                    *w = 0;
-                    if (runScriptDebug > 1) printf("simple expression %s\n", s);
-                }
-                else if (*r == ',')
-                {
-                    /* single comma */
-                    *w++ = *r++;
-                }
-                else {
-                    /* unquoted string (i.e plain word) */
-                    do {
-                        *w++ = *r++;
-                    } while (*r && !strchr("%(\"', \t\n", *r));
-                    *w = 0;
-                    if (runScriptDebug > 1) printf("plain word '%s'\n", s);
-                }
-                /* copy space */
-                while (isspace((unsigned char)*r)) *w++ = *r++;
-                /* terminate */
-                *w = 0;
-            }
+            replaceExpressions(x, line_raw, line_raw_size);
             if (runScriptDebug)
                 printf("runScript: assign %s=%s\n", p, line_raw);
             macPutValue(mac, p, line_raw);
@@ -596,6 +344,7 @@ int afterInit(char* cmd, char* a1, char* a2, char* a3, char* a4, char* a5, char*
 }
 
 epicsExportAddress(int, runScriptDebug);
+epicsExportAddress(int, exprDebug);
 
 static const iocshFuncDef runScriptDef = {
     "runScript", 2, (const iocshArg *[]) {
diff --git a/runScript.dbd b/runScript.dbd
index a16c6c234ee2e199481112243cedeb2bfa66b388..ab7b21dc3715dc551931416a2a39d9e7719e02a3 100644
--- a/runScript.dbd
+++ b/runScript.dbd
@@ -1,2 +1,3 @@
 registrar(runScriptRegister)
 variable(runScriptDebug,int)
+variable(exprDebug,int)