Re: mod_lua and subprocess_env

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Mar 22, 2017 at 1:37 PM, Yann Ylavic <ylavic.dev@xxxxxxxxx> wrote:
>
> There are two patches attached, one for the changes in httpd code, the
> other for the files generated by the bison/flex parser.

The second patch was missing the changes in server/util_expr_parse.h,
resending...

>
> Hope that helps,
> Yann.
Index: server/util_expr_private.h
===================================================================
--- server/util_expr_private.h	(revision 1783852)
+++ server/util_expr_private.h	(working copy)
@@ -54,9 +54,10 @@ typedef enum {
     op_REG, op_NRE,
     op_STR_EQ, op_STR_NE, op_STR_LT, op_STR_LE, op_STR_GT, op_STR_GE,
     op_Concat,
-    op_Digit, op_String, op_Regex, op_RegexBackref,
-    op_Var,
-    op_ListElement,
+    op_Digit, op_String,
+    op_Var, op_Word, op_Bool, op_Join,
+    op_Regex, op_Regsub, op_Regref,
+    op_ListElement, op_ListRegex,
     /*
      * call external functions/operators.
      * The info node contains the function pointer and some function specific
@@ -79,6 +80,15 @@ struct ap_expr_node {
     const void *node_arg2;
 };
 
+/** The stack used by scanner and parser */
+typedef struct ap_expr_parser_stack {
+    char *scan_ptr;
+    char  scan_buf[MAX_STRING_LEN];
+    char  scan_del;
+    int   scan_state;
+    struct ap_expr_parser_stack *next;
+} ap_expr_parser_stack_t;
+
 /** The context used by scanner and parser */
 typedef struct {
     /* internal state of the scanner */
@@ -86,9 +96,8 @@ typedef struct {
     int                inputlen;
     const char        *inputptr;
     void              *scanner;
-    char              *scan_ptr;
-    char               scan_buf[MAX_STRING_LEN];
-    char               scan_del;
+    ap_expr_parser_stack_t *stack;
+    ap_expr_parser_stack_t *stacks;
     int                at_start;
 
     /* pools for result and temporary usage */
@@ -119,6 +128,13 @@ void ap_expr_yyset_extra(ap_expr_parse_ctx_t *cont
 /* create a parse tree node */
 ap_expr_t *ap_expr_make(ap_expr_node_op_e op, const void *arg1,
                         const void *arg2, ap_expr_parse_ctx_t *ctx);
+ap_expr_t *ap_expr_regex_make(const char *pattern, const char *flags,
+                              const ap_expr_t *subst, int split,
+                              ap_expr_parse_ctx_t *ctx);
+ap_expr_t *ap_expr_str_word_make(const ap_expr_t *arg,
+                                 ap_expr_parse_ctx_t *ctx);
+ap_expr_t *ap_expr_str_bool_make(const ap_expr_t *arg,
+                                 ap_expr_parse_ctx_t *ctx);
 /* create parse tree node for the string-returning function 'name' */
 ap_expr_t *ap_expr_str_func_make(const char *name, const ap_expr_t *arg,
                                ap_expr_parse_ctx_t *ctx);
@@ -125,6 +141,8 @@ ap_expr_t *ap_expr_str_func_make(const char *name,
 /* create parse tree node for the list-returning function 'name' */
 ap_expr_t *ap_expr_list_func_make(const char *name, const ap_expr_t *arg,
                                 ap_expr_parse_ctx_t *ctx);
+ap_expr_t *ap_expr_list_regex_make(const ap_expr_t *lst, const ap_expr_t *re,
+                                   ap_expr_parse_ctx_t *ctx);
 /* create parse tree node for the variable 'name' */
 ap_expr_t *ap_expr_var_make(const char *name, ap_expr_parse_ctx_t *ctx);
 /* create parse tree node for the unary operator 'name' */
@@ -135,6 +153,9 @@ ap_expr_t *ap_expr_binary_op_make(const char *name
                                   const ap_expr_t *arg2,
                                   ap_expr_parse_ctx_t *ctx);
 
+/* push/pop a stack in the parser/lexer */
+void ap_expr_parser_stack_push(ap_expr_parse_ctx_t *ctx);
+void ap_expr_parser_stack_pop(ap_expr_parse_ctx_t *ctx);
 
 #endif /* __AP_EXPR_PRIVATE_H__ */
 /** @} */
Index: server/util_expr_eval.c
===================================================================
--- server/util_expr_eval.c	(revision 1783852)
+++ server/util_expr_eval.c	(working copy)
@@ -26,6 +26,7 @@
 #include "ap_provider.h"
 #include "util_expr_private.h"
 #include "util_md5.h"
+#include "util_varbuf.h"
 
 #include "apr_lib.h"
 #include "apr_fnmatch.h"
@@ -47,6 +48,8 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(int, expr_lookup, (ap_
 
 #define  LOG_MARK(info)  __FILE__, __LINE__, (info)->module_index
 
+static int ap_expr_eval(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node);
+
 static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
                                             const ap_expr_t *info,
                                             const ap_expr_t *args);
@@ -56,6 +59,19 @@ static const char *ap_expr_eval_var(ap_expr_eval_c
                                     ap_expr_var_func_t *func,
                                     const void *data);
 
+typedef struct {
+    int type, flags;
+    const ap_expr_t *subst;
+} ap_expr_regctx_t;
+
+static const char *ap_expr_regexec(const char *subject,
+                                   const ap_expr_t *reg,
+                                   apr_array_header_t *list,
+                                   ap_expr_eval_ctx_t *ctx);
+
+static apr_array_header_t *ap_expr_list_make(ap_expr_eval_ctx_t *ctx,
+                                             const ap_expr_t *node);
+
 /* define AP_EXPR_DEBUG to log the parse tree when parsing an expression */
 #ifdef AP_EXPR_DEBUG
 static void expr_dump_tree(const ap_expr_t *e, const server_rec *s,
@@ -80,6 +96,37 @@ static int inc_rec(ap_expr_eval_ctx_t *ctx)
     return 1;
 }
 
+static const char *ap_expr_list_pstrcat(apr_pool_t *p,
+                                        const apr_array_header_t *list,
+                                        const char *sep)
+{
+    if (apr_is_empty_array(list)) {
+        return NULL;
+    }
+    else if (list->nelts == 1) {
+        return APR_ARRAY_IDX(list, 0, const char*);
+    }
+    else {
+        struct ap_varbuf vb;
+        int n = list->nelts - 1, i;
+        apr_size_t slen = strlen(sep), vlen;
+        const char *val;
+
+        ap_varbuf_init(p, &vb, 0);
+        for (i = 0; i < n; ++i) {
+            val = APR_ARRAY_IDX(list, i, const char*);
+            vlen = strlen(val);
+            ap_varbuf_grow(&vb, vlen + slen + 1);
+            ap_varbuf_strmemcat(&vb, val, vlen);
+            ap_varbuf_strmemcat(&vb, sep, slen);
+        }
+        val = APR_ARRAY_IDX(list, n, const char*);
+        ap_varbuf_strmemcat(&vb, val, strlen(val));
+
+        return vb.buf;
+    }
+}
+
 static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
                                      const ap_expr_t *node)
 {
@@ -91,6 +138,12 @@ static const char *ap_expr_eval_word(ap_expr_eval_
     case op_String:
         result = node->node_arg1;
         break;
+    case op_Word:
+        result = ap_expr_eval_word(ctx, node->node_arg1);
+        break;
+    case op_Bool:
+        result = ap_expr_eval(ctx, node->node_arg1) ? "true" : "false";
+        break;
     case op_Var:
         result = ap_expr_eval_var(ctx, (ap_expr_var_func_t *)node->node_arg1,
                                   node->node_arg2);
@@ -161,7 +214,20 @@ static const char *ap_expr_eval_word(ap_expr_eval_
         result = ap_expr_eval_string_func(ctx, info, args);
         break;
     }
-    case op_RegexBackref: {
+    case op_Join: {
+        const char *sep;
+        apr_array_header_t *list = ap_expr_list_make(ctx, node->node_arg1);
+        sep = node->node_arg2 ? ap_expr_eval_word(ctx, node->node_arg2) : "";
+        result = ap_expr_list_pstrcat(ctx->p, list, sep);
+        break;
+    }
+    case op_Regsub: {
+        const ap_expr_t *reg = node->node_arg2;
+        const char *subject = ap_expr_eval_word(ctx, node->node_arg1);
+        result = ap_expr_regexec(subject, reg, NULL, ctx);
+        break;
+    }
+    case op_Regref: {
         const unsigned int *np = node->node_arg1;
         result = ap_expr_eval_re_backref(ctx, *np);
         break;
@@ -226,6 +292,170 @@ static int intstrcmp(const char *s1, const char *s
         return 1;
 }
 
+static const char *ap_expr_regexec(const char *subject,
+                                   const ap_expr_t *reg,
+                                   apr_array_header_t *list,
+                                   ap_expr_eval_ctx_t *ctx)
+{
+    struct ap_varbuf vb;
+    const char *val = subject;
+    const ap_regex_t *regex = reg->node_arg1;
+    const ap_expr_regctx_t *regctx = reg->node_arg2;
+    ap_regmatch_t *pmatch = NULL, match0;
+    apr_size_t nmatch = 0;
+    const char *str = "";
+    apr_size_t len = 0;
+    int empty = 0, rv;
+
+    ap_varbuf_init(ctx->p, &vb, 0);
+    if (ctx->re_nmatch > 0) {
+        nmatch = ctx->re_nmatch;
+        pmatch = ctx->re_pmatch;
+    }
+    else if (regctx->type != 'm') {
+        nmatch = 1;
+        pmatch = &match0;
+    }
+    do {
+        /* If previous match was empty, we can't issue the exact same one or
+         * we'd loop indefinitively.  So let's instead ask for an anchored and
+         * non-empty match (i.e. something not empty at the start of the value)
+         * and if nothing is found advance by one character below.
+         */
+        rv = ap_regexec(regex, val, nmatch, pmatch, 
+                        empty ? AP_REG_ANCHORED | AP_REG_NOTEMPTY : 0);
+        if (regctx->type == 'm') {
+            /* Simple match "m//", just return whether it matched (subject)
+             * or not (NULL) 
+             */
+            return (rv == 0) ? subject : NULL;
+        }
+        if (rv == 0) {
+            /* Substitution "s//" or split "S//" matched.
+             * s// => replace $0 with evaluated regctx->subst
+             * S// => split at $0 (keeping evaluated regctx->subst if any)
+             */
+            int pos = pmatch[0].rm_so,
+                end = pmatch[0].rm_eo;
+            AP_DEBUG_ASSERT(pos >= 0 && pos <= end);
+
+            if (regctx->subst) {
+                *ctx->re_source = val;
+                str = ap_expr_eval_word(ctx, regctx->subst);
+                len = strlen(str);
+            }
+            /* Splitting makes sense into a given list only, if NULL we fall
+             * back into returning a s// string...
+             */
+            if (list) {
+                char *tmp = apr_palloc(ctx->p, pos + len + 1);
+                memcpy(tmp, val, pos);
+                memcpy(tmp + pos, str, len);
+                tmp[pos + len] = '\0';
+                APR_ARRAY_PUSH(list, const char*) = tmp;
+            }
+            else { /* regctx->type == 's' */
+                ap_varbuf_grow(&vb, pos + len + 1);
+                ap_varbuf_strmemcat(&vb, val, pos);
+                ap_varbuf_strmemcat(&vb, str, len);
+                if (!(regctx->flags & AP_REG_MULTI)) {
+                    /* Single substitution, preserve remaining data */
+                    ap_varbuf_strmemcat(&vb, val + end, strlen(val) - end);
+                    break;
+                }
+            }
+            /* Note an empty match */
+            empty = (end == 0);
+            val += end;
+        }
+        else if (empty) {
+            /* Skip this non-matching character (or CRLF) and restart
+             * another "normal" match (possibly empty) from there.
+             */
+            if (val[0] == APR_ASCII_CR && val[1] == APR_ASCII_LF) {
+                val += 2;
+            }
+            else {
+                val++;
+            }
+            empty = 0;
+        }
+        else {
+            if (list) {
+                APR_ARRAY_PUSH(list, const char*) = val;
+            }
+            else if (vb.avail) {
+                ap_varbuf_strmemcat(&vb, val, strlen(val));
+            }
+            else {
+                return val;
+            }
+            break;
+        }
+    } while (*val);
+
+    return vb.buf;
+}
+
+static apr_array_header_t *ap_expr_list_make(ap_expr_eval_ctx_t *ctx,
+                                             const ap_expr_t *node)
+{
+    apr_array_header_t *list = NULL;
+
+    if (node->node_op == op_ListRegex) {
+        const ap_expr_t *arg = node->node_arg1;
+        const ap_expr_t *reg = node->node_arg2;
+        const ap_expr_regctx_t *regctx = reg->node_arg2;
+        const apr_array_header_t *source = ap_expr_list_make(ctx, arg);
+        int i;
+
+        list = apr_array_make(ctx->p, source->nelts, sizeof(const char*));
+        for (i = 0; i < source->nelts; ++i) {
+            const char *val = APR_ARRAY_IDX(source, i, const char*);
+            if (regctx->type == 'S') {
+                (void)ap_expr_regexec(val, reg, list, ctx);
+            }
+            else {
+                val = ap_expr_regexec(val, reg, NULL, ctx);
+                if (val) {
+                    APR_ARRAY_PUSH(list, const char*) = val;
+                }
+            }
+        }
+    }
+    else if (node->node_op == op_ListElement) {
+        int n = 0;
+        const ap_expr_t *elem;
+        for (elem = node; elem; elem = elem->node_arg2) {
+            AP_DEBUG_ASSERT(elem->node_op == op_ListElement);
+            n++;
+        }
+
+        list = apr_array_make(ctx->p, n, sizeof(const char*));
+        for (elem = node; elem; elem = elem->node_arg2) {
+            APR_ARRAY_PUSH(list, const char*) =
+                ap_expr_eval_word(ctx, elem->node_arg1);
+        }
+    }
+    else if (node->node_op == op_ListFuncCall) {
+        const ap_expr_t *info = node->node_arg1;
+        ap_expr_list_func_t *func = info->node_arg1;
+
+        AP_DEBUG_ASSERT(func != NULL);
+        AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
+        list = (*func)(ctx, info->node_arg2,
+                       ap_expr_eval_word(ctx, node->node_arg2));
+    }
+    else {
+        const char *subject = ap_expr_eval_word(ctx, node);
+
+        list = apr_array_make(ctx->p, 8, sizeof(const char*));
+        (void)ap_expr_regexec(subject, node->node_arg2, list, ctx);
+    }
+
+    return list;
+}
+
 static int ap_expr_eval_comp(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
 {
     const ap_expr_t *e1 = node->node_arg1;
@@ -256,31 +486,18 @@ static int ap_expr_eval_comp(ap_expr_eval_ctx_t *c
     case op_STR_GE:
         return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
     case op_IN: {
-            const char *needle = ap_expr_eval_word(ctx, e1);
-            if (e2->node_op == op_ListElement) {
-                do {
-                    const ap_expr_t *val = e2->node_arg1;
-                    AP_DEBUG_ASSERT(e2->node_op == op_ListElement);
-                    if (strcmp(needle, ap_expr_eval_word(ctx, val)) == 0)
+            int n;
+            const char *needle, *subject;
+            apr_array_header_t *haystack;
+            haystack = ap_expr_list_make(ctx, e2);
+            if (haystack) {
+                needle = ap_expr_eval_word(ctx, e1);
+                for (n = 0; n < haystack->nelts; ++n) {
+                    subject = APR_ARRAY_IDX(haystack, n, const char*);
+                    if (strcmp(needle, subject) == 0) {
                         return 1;
-                    e2 = e2->node_arg2;
-                } while (e2 != NULL);
-            }
-            else if (e2->node_op == op_ListFuncCall) {
-                const ap_expr_t *info = e2->node_arg1;
-                const ap_expr_t *arg = e2->node_arg2;
-                ap_expr_list_func_t *func = (ap_expr_list_func_t *)info->node_arg1;
-                apr_array_header_t *haystack;
-
-                AP_DEBUG_ASSERT(func != NULL);
-                AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
-                haystack = (*func)(ctx, info->node_arg2, ap_expr_eval_word(ctx, arg));
-                if (haystack == NULL) {
-                    return 0;
+                    }
                 }
-                if (ap_array_str_contains(haystack, needle)) {
-                    return 1;
-                }
             }
             return 0;
         }
@@ -303,10 +520,7 @@ static int ap_expr_eval_comp(ap_expr_eval_ctx_t *c
                 result = (0 == ap_regexec(regex, word, 0, NULL, 0));
             }
 
-            if (node->node_op == op_REG)
-                return result;
-            else
-                return !result;
+            return result ^ (node->node_op == op_NRE);
         }
     default:
         *ctx->err = "Internal evaluation error: Unknown comp expression node";
@@ -387,9 +601,8 @@ AP_DECLARE(const char *) ap_expr_parse(apr_pool_t
     ctx.error    = NULL;        /* generic bison error message (XXX: usually not very useful, should be axed) */
     ctx.error2   = NULL;        /* additional error message */
     ctx.flags    = info->flags;
-    ctx.scan_del    = '\0';
-    ctx.scan_buf[0] = '\0';
-    ctx.scan_ptr    = ctx.scan_buf;
+    ctx.stack    = NULL;
+    ctx.stacks   = NULL;
     ctx.lookup_fn   = lookup_fn ? lookup_fn : ap_expr_lookup_default;
     ctx.at_start    = 1;
 
@@ -450,6 +663,80 @@ ap_expr_t *ap_expr_make(ap_expr_node_op_e op, cons
     return node;
 }
 
+ap_expr_t *ap_expr_regex_make(const char *pattern, const char *flags,
+                              const ap_expr_t *subst, int split,
+                              ap_expr_parse_ctx_t *ctx)
+{
+    ap_expr_t *node = NULL;
+    ap_expr_regctx_t *regctx;
+    ap_regex_t *regex;
+
+    regctx = apr_palloc(ctx->pool, sizeof *regctx);
+    regctx->flags = 0;
+    if (flags) {
+        for (; *flags; ++flags) {
+            switch (*flags) {
+            case 'i':
+                regctx->flags |= AP_REG_ICASE;
+                break;
+            case 'm':
+                regctx->flags |= AP_REG_NEWLINE;
+                break;
+            case 's':
+                regctx->flags |= AP_REG_DOTALL;
+                break;
+            case 'g':
+                regctx->flags |= AP_REG_MULTI;
+                break;
+            }
+        }
+    }
+    regctx->subst = subst;
+    if (subst) {
+        if (split) {
+            regctx->type = 'S';
+            regctx->flags |= AP_REG_MULTI;
+        }
+        else {
+            regctx->type = 's';
+        }
+    }
+    else {
+        regctx->type = 'm';
+    }
+
+    regex = ap_pregcomp(ctx->pool, pattern, regctx->flags);
+    if (!regex) {
+        return NULL;
+    }
+
+    node = apr_palloc(ctx->pool, sizeof(ap_expr_t));
+    node->node_op   = op_Regex;
+    node->node_arg1 = regex;
+    node->node_arg2 = regctx;
+    return node;
+}
+
+ap_expr_t *ap_expr_str_word_make(const ap_expr_t *arg,
+                                 ap_expr_parse_ctx_t *ctx)
+{
+    ap_expr_t *node = apr_palloc(ctx->pool, sizeof(ap_expr_t));
+    node->node_op   = op_Word;
+    node->node_arg1 = arg;
+    node->node_arg2 = NULL;
+    return node;
+}
+
+ap_expr_t *ap_expr_str_bool_make(const ap_expr_t *arg,
+                                 ap_expr_parse_ctx_t *ctx)
+{
+    ap_expr_t *node = apr_palloc(ctx->pool, sizeof(ap_expr_t));
+    node->node_op   = op_Bool;
+    node->node_arg1 = arg;
+    node->node_arg2 = NULL;
+    return node;
+}
+
 static ap_expr_t *ap_expr_info_make(int type, const char *name,
                                   ap_expr_parse_ctx_t *ctx,
                                   const ap_expr_t *arg)
@@ -492,6 +779,16 @@ ap_expr_t *ap_expr_list_func_make(const char *name
     return ap_expr_make(op_ListFuncCall, info, arg, ctx);
 }
 
+ap_expr_t *ap_expr_list_regex_make(const ap_expr_t *arg, const ap_expr_t *reg,
+                                   ap_expr_parse_ctx_t *ctx)
+{
+    ap_expr_t *node = apr_palloc(ctx->pool, sizeof(ap_expr_t));
+    node->node_op   = op_ListRegex;
+    node->node_arg1 = arg;
+    node->node_arg2 = reg;
+    return node;
+}
+
 ap_expr_t *ap_expr_unary_op_make(const char *name, const ap_expr_t *arg,
                                ap_expr_parse_ctx_t *ctx)
 {
@@ -613,10 +910,15 @@ static void expr_dump_tree(const ap_expr_t *e, con
     case op_IN:
     case op_REG:
     case op_NRE:
+    case op_Word:
+    case op_Bool:
+    case op_Join:
+    case op_Regsub:
     case op_Concat:
     case op_StringFuncCall:
     case op_ListFuncCall:
     case op_ListElement:
+    case op_ListRegex:
         {
             char *name;
             switch (e->node_op) {
@@ -639,10 +941,15 @@ static void expr_dump_tree(const ap_expr_t *e, con
             CASE_OP(op_IN);
             CASE_OP(op_REG);
             CASE_OP(op_NRE);
+            CASE_OP(op_Word);
+            CASE_OP(op_Bool);
+            CASE_OP(op_Join);
+            CASE_OP(op_Regsub);
             CASE_OP(op_Concat);
             CASE_OP(op_StringFuncCall);
             CASE_OP(op_ListFuncCall);
             CASE_OP(op_ListElement);
+            CASE_OP(op_ListRegex);
             default:
                 ap_assert(0);
             }
@@ -688,8 +995,8 @@ static void expr_dump_tree(const ap_expr_t *e, con
         DUMP_P("op_Regex", e->node_arg1);
         break;
     /* arg1: pointer to int */
-    case op_RegexBackref:
-        DUMP_IP("op_RegexBackref", e->node_arg1);
+    case op_Regref:
+        DUMP_IP("op_Regref", e->node_arg1);
         break;
     default:
         ap_log_error(MARK, "%*sERROR: INVALID OP %d", indent, " ", e->node_op);
@@ -1804,3 +2111,32 @@ void ap_expr_init(apr_pool_t *p)
     ap_hook_post_config(ap_expr_post_config, NULL, NULL, APR_HOOK_MIDDLE);
 }
 
+void ap_expr_parser_stack_push(ap_expr_parse_ctx_t *ctx)
+{
+    ap_expr_parser_stack_t *stack;
+
+    if (ctx->stacks) {
+        stack = ctx->stacks;
+        ctx->stacks = stack->next;
+    }
+    else {
+        stack = apr_palloc(ctx->ptemp, sizeof *stack);
+    }
+    stack->scan_ptr    = stack->scan_buf;
+    stack->scan_buf[0] = '\0';
+    stack->scan_del    = '\0';
+    stack->scan_state  = 0;
+
+    stack->next = ctx->stack;
+    ctx->stack = stack;
+}
+
+void ap_expr_parser_stack_pop(ap_expr_parse_ctx_t *ctx)
+{
+    ap_expr_parser_stack_t *stack;
+
+    stack = ctx->stack;
+    ctx->stack = stack->next;
+    stack->next = ctx->stacks;
+    ctx->stacks = stack;
+}
Index: server/util_expr_parse.y
===================================================================
--- server/util_expr_parse.y	(revision 1783852)
+++ server/util_expr_parse.y	(working copy)
@@ -49,9 +49,14 @@
 %token  <cpVal> T_DIGIT
 %token  <cpVal> T_ID
 %token  <cpVal> T_STRING
-%token  <cpVal> T_REGEX
-%token  <cpVal> T_REGEX_I
-%token  <num>   T_REGEX_BACKREF
+
+%token          T_REGEX
+%token          T_REGSUB
+%token  <cpVal> T_REG_MATCH
+%token  <cpVal> T_REG_SUBST
+%token  <cpVal> T_REG_FLAGS
+%token  <num>   T_REG_REF
+
 %token  <cpVal> T_OP_UNARY
 %token  <cpVal> T_OP_BINARY
 
@@ -77,6 +82,9 @@
 %token  T_OP_STR_GE
 %token  T_OP_CONCAT
 
+%token  T_OP_SPLIT
+%token  T_OP_JOIN
+
 %token  T_OP_OR
 %token  T_OP_AND
 %token  T_OP_NOT
@@ -86,11 +94,10 @@
 %right  T_OP_NOT
 %right  T_OP_CONCAT
 
-%type   <exVal>   expr
-%type   <exVal>   comparison
+%type   <exVal>   cond
+%type   <exVal>   comp
 %type   <exVal>   strfunccall
 %type   <exVal>   lstfunccall
-%type   <exVal>   regex
 %type   <exVal>   words
 %type   <exVal>   wordlist
 %type   <exVal>   word
@@ -97,7 +104,11 @@
 %type   <exVal>   string
 %type   <exVal>   strpart
 %type   <exVal>   var
-%type   <exVal>   backref
+%type   <exVal>   regex
+%type   <exVal>   regsub
+%type   <exVal>   regsplit
+%type   <exVal>   reglist
+%type   <exVal>   regref
 
 %{
 #include "util_expr_private.h"
@@ -109,24 +120,22 @@ int ap_expr_yylex(YYSTYPE *lvalp, void *scanner);
 
 %%
 
-root      : T_EXPR_BOOL   expr           { ctx->expr = $2; }
+root      : T_EXPR_BOOL   cond           { ctx->expr = $2; }
           | T_EXPR_STRING string         { ctx->expr = $2; }
           | T_ERROR                      { YYABORT; }
           ;
 
-expr      : T_TRUE                       { $$ = ap_expr_make(op_True,        NULL, NULL, ctx); }
+cond      : T_TRUE                       { $$ = ap_expr_make(op_True,        NULL, NULL, ctx); }
           | T_FALSE                      { $$ = ap_expr_make(op_False,       NULL, NULL, ctx); }
-          | T_OP_NOT expr                { $$ = ap_expr_make(op_Not,         $2,   NULL, ctx); }
-          | expr T_OP_OR expr            { $$ = ap_expr_make(op_Or,          $1,   $3,   ctx); }
-          | expr T_OP_AND expr           { $$ = ap_expr_make(op_And,         $1,   $3,   ctx); }
-          | comparison                   { $$ = ap_expr_make(op_Comp,        $1,   NULL, ctx); }
-          | T_OP_UNARY word              { $$ = ap_expr_unary_op_make(       $1,   $2,   ctx); }
-          | word T_OP_BINARY word        { $$ = ap_expr_binary_op_make($2,   $1,   $3,   ctx); }
-          | '(' expr ')'                 { $$ = $2; }
+          | cond T_OP_OR cond            { $$ = ap_expr_make(op_Or,          $1,   $3,   ctx); }
+          | cond T_OP_AND cond           { $$ = ap_expr_make(op_And,         $1,   $3,   ctx); }
+          | T_OP_NOT cond                { $$ = ap_expr_make(op_Not,         $2,   NULL, ctx); }
+          | comp                         { $$ = ap_expr_make(op_Comp,        $1,   NULL, ctx); }
+          | '(' cond ')'                 { $$ = $2; }
           | T_ERROR                      { YYABORT; }
           ;
 
-comparison: word T_OP_EQ word            { $$ = ap_expr_make(op_EQ,      $1, $3, ctx); }
+comp      : word T_OP_EQ word            { $$ = ap_expr_make(op_EQ,      $1, $3, ctx); }
           | word T_OP_NE word            { $$ = ap_expr_make(op_NE,      $1, $3, ctx); }
           | word T_OP_LT word            { $$ = ap_expr_make(op_LT,      $1, $3, ctx); }
           | word T_OP_LE word            { $$ = ap_expr_make(op_LE,      $1, $3, ctx); }
@@ -141,10 +150,15 @@ int ap_expr_yylex(YYSTYPE *lvalp, void *scanner);
           | word T_OP_IN wordlist        { $$ = ap_expr_make(op_IN,      $1, $3, ctx); }
           | word T_OP_REG regex          { $$ = ap_expr_make(op_REG,     $1, $3, ctx); }
           | word T_OP_NRE regex          { $$ = ap_expr_make(op_NRE,     $1, $3, ctx); }
+          | word T_OP_BINARY word        { $$ = ap_expr_binary_op_make($2, $1, $3, ctx); }
+          | T_OP_UNARY word              { $$ = ap_expr_unary_op_make(     $1, $2, ctx); }
           ;
 
 wordlist  : lstfunccall                  { $$ = $1; }
           | '{' words '}'                { $$ = $2; }
+          | word     T_OP_REG regsplit   { $$ = ap_expr_list_regex_make($1, $3, ctx); }
+          | wordlist T_OP_REG reglist    { $$ = ap_expr_list_regex_make($1, $3, ctx); }
+          | '(' wordlist ')'             { $$ = $2; }
           ;
 
 words     : word                         { $$ = ap_expr_make(op_ListElement, $1, NULL, ctx); }
@@ -151,53 +165,78 @@ words     : word                         { $$ = ap
           | words ',' word               { $$ = ap_expr_make(op_ListElement, $3, $1,   ctx); }
           ;
 
-string    : string strpart               { $$ = ap_expr_make(op_Concat, $1, $2, ctx); }
-          | strpart                      { $$ = $1; }
+string    : strpart                      { $$ = $1; }
+          | string strpart               { $$ = $2 ? ap_expr_make(op_Concat, $1, $2, ctx) : $1; }
           | T_ERROR                      { YYABORT; }
           ;
 
-strpart   : T_STRING                     { $$ = ap_expr_make(op_String, $1, NULL, ctx); }
+strpart   : T_STRING                     { $$ = $1 ? ap_expr_make(op_String, $1, NULL, ctx) : NULL; }
           | var                          { $$ = $1; }
-          | backref                      { $$ = $1; }
+          | regref                       { $$ = $1; }
           ;
 
 var       : T_VAR_BEGIN T_ID T_VAR_END            { $$ = ap_expr_var_make($2, ctx); }
           | T_VAR_BEGIN T_ID ':' string T_VAR_END { $$ = ap_expr_str_func_make($2, $4, ctx); }
+          | T_VAR_BEGIN ':' word ':' T_VAR_END    { $$ = ap_expr_str_word_make($3, ctx); }
           ;
 
-word      : T_DIGIT                      { $$ = ap_expr_make(op_Digit,  $1, NULL, ctx); }
-          | word T_OP_CONCAT word        { $$ = ap_expr_make(op_Concat, $1, $3,   ctx); }
+word      : word T_OP_CONCAT word        { $$ = ap_expr_make(op_Concat, $1, $3,   ctx); }
+          | word T_OP_REG regsub         { $$ = ap_expr_make(op_Regsub, $1, $3,   ctx); }
+          | cond                         { $$ = ap_expr_str_bool_make(  $1,       ctx); }
           | var                          { $$ = $1; }
-          | backref                      { $$ = $1; }
+          | regref                       { $$ = $1; }
           | strfunccall                  { $$ = $1; }
+          | T_OP_JOIN '/' wordlist '/'      '/' { $$ = ap_expr_make(op_Join,   $3, NULL, ctx); }
+          | T_OP_JOIN '/' wordlist '/' word '/' { $$ = ap_expr_make(op_Join,   $3, $5,   ctx); }
+          | T_DIGIT                      { $$ = ap_expr_make(op_Digit,  $1, NULL, ctx); }
+          | T_STR_BEGIN T_STR_END        { $$ = ap_expr_make(op_String, "", NULL, ctx); }
           | T_STR_BEGIN string T_STR_END { $$ = $2; }
-          | T_STR_BEGIN T_STR_END        { $$ = ap_expr_make(op_String, "", NULL, ctx); }
+          | '(' word ')'                 { $$ = $2; }
           ;
-
-regex     : T_REGEX {
-                ap_regex_t *regex;
-                if ((regex = ap_pregcomp(ctx->pool, $1,
-                                         AP_REG_EXTENDED|AP_REG_NOSUB)) == NULL) {
+regex     : T_REGEX T_REG_MATCH T_REG_FLAGS {
+                ap_expr_t *e = ap_expr_regex_make($2, $3, NULL, 0, ctx);
+                if (!e) {
                     ctx->error = "Failed to compile regular expression";
                     YYERROR;
                 }
-                $$ = ap_expr_make(op_Regex, regex, NULL, ctx);
+                $$ = e;
             }
-          | T_REGEX_I {
-                ap_regex_t *regex;
-                if ((regex = ap_pregcomp(ctx->pool, $1,
-                                         AP_REG_EXTENDED|AP_REG_NOSUB|AP_REG_ICASE)) == NULL) {
+          ;
+regsub    : T_REGSUB T_REG_MATCH string T_REG_FLAGS {
+                ap_expr_t *e = ap_expr_regex_make($2, $4, $3, 0, ctx);
+                if (!e) {
                     ctx->error = "Failed to compile regular expression";
                     YYERROR;
                 }
-                $$ = ap_expr_make(op_Regex, regex, NULL, ctx);
+                $$ = e;
             }
           ;
+regsplit  : T_OP_SPLIT T_REG_MATCH string T_REG_FLAGS {
+                /* Returns a list:
+                 * <word> ~= split/://
+                 *  => split around ':'
+                 * <word> ~= split/:/\n/
+                 *  => split around ':', use '\n' instead where detected/stripped
+                 * <list> ~= split/.*?Ip Address:([^,]+)/$1/
+                 *  => split around the whole match, use $1 instead where detected/stipped
+                 */
+                ap_expr_t *e = ap_expr_regex_make($2, $4, $3, 1, ctx);
+                if (!e) {
+                    ctx->error = "Failed to compile regular expression";
+                    YYERROR;
+                }
+                $$ = e;
+            }
+          ;
+reglist   : regex     { $$ = $1; }
+          | regsub    { $$ = $1; }
+          | regsplit  { $$ = $1; }
+          ;
 
-backref     : T_REGEX_BACKREF   {
+regref      : T_REG_REF {
                 int *n = apr_palloc(ctx->pool, sizeof(int));
                 *n = $1;
-                $$ = ap_expr_make(op_RegexBackref, n, NULL, ctx);
+                $$ = ap_expr_make(op_Regref, n, NULL, ctx);
             }
             ;
 
Index: server/util_expr_scan.l
===================================================================
--- server/util_expr_scan.l	(revision 1783852)
+++ server/util_expr_scan.l	(working copy)
@@ -34,14 +34,16 @@
 %option warn
 %option noinput nounput noyy_top_state
 %option stack
+
 %x str
-%x var
-%x vararg
-%x regex regex_flags
+%x var vararg varext
+%x regex regsub regflags
+%x split join
 
 %{
 #include "util_expr_private.h"
 #include "util_expr_parse.h"
+#include "apr_lib.h" /* apr_isalnum */
 
 #undef  YY_INPUT
 #define YY_INPUT(buf,result,max_size)                       \
@@ -62,10 +64,57 @@
 
 #define PERROR(msg) do { yyextra->error2 = msg ; return T_ERROR; } while (0)
 
-#define str_ptr     (yyextra->scan_ptr)
-#define str_buf     (yyextra->scan_buf)
-#define str_del     (yyextra->scan_del)
+#if 0
+#define STACK_PUSH() do { \
+    fprintf(stderr, "YY_STACK_PUSH()\n"); \
+    ap_expr_parser_stack_push(yyextra); \
+} while (0)
+#define STACK_POP() do { \
+    fprintf(stderr, "YY_STACK_POP()\n"); \
+    ap_expr_parser_stack_pop(yyextra); \
+} while (0)
 
+#define STATE_PUSH(st, sk) do { \
+    fprintf(stderr, "YY_STATE_PUSH(%i)\n", (st)); \
+    yy_push_state((st), yyscanner); \
+    if (sk) { \
+        STACK_PUSH(); \
+    } \
+} while (0)
+#define STATE_POP(sk) do { \
+    fprintf(stderr, "YY_STATE_POP()\n"); \
+    if (sk) { \
+        STACK_POP(); \
+    } \
+    yy_pop_state(yyscanner); \
+} while (0)
+#else
+#define STACK_PUSH() do { \
+    ap_expr_parser_stack_push(yyextra); \
+} while (0)
+#define STACK_POP() do { \
+    ap_expr_parser_stack_pop(yyextra); \
+} while (0)
+
+#define STATE_PUSH(st, sk) do { \
+    yy_push_state((st), yyscanner); \
+    if (sk) { \
+        STACK_PUSH(); \
+    } \
+} while (0)
+#define STATE_POP(sk) do { \
+    if (sk) { \
+        STACK_POP(); \
+    } \
+    yy_pop_state(yyscanner); \
+} while (0)
+#endif
+
+#define str_ptr     (yyextra->stack->scan_ptr)
+#define str_buf     (yyextra->stack->scan_buf)
+#define str_del     (yyextra->stack->scan_del)
+#define str_state   (yyextra->stack->scan_state)
+
 #define STR_APPEND(c) do {                          \
         *str_ptr++ = (c);                           \
         if (str_ptr >= str_buf + sizeof(str_buf))   \
@@ -72,15 +121,22 @@
             PERROR("String too long");              \
     } while (0)
 
+#define STR_EMPTY() (str_ptr == str_buf)
+#define STR_FINAL() (*str_ptr = '\0', str_ptr = str_buf)
+
 %}
 
+SPACE     [ \t\n]
+QUOTE     ["']
+TOKEN     ([a-zA-Z][a-zA-Z0-9_]*)
+VAR_BEGIN (%\{)
+VAR_SEP   :
+VAR_END   \}
+REG_SEP   [/#$%^|?!'",;:._-]
+REG_REF   (\$[0-9])
 
 %%
 
-  char  regex_buf[MAX_STRING_LEN];
-  char *regex_ptr = NULL;
-  char  regex_del = '\0';
-
 %{
  /*
   * Set initial state for string expressions
@@ -88,7 +144,7 @@
   if (yyextra->at_start) {
     yyextra->at_start = 0;
     if (yyextra->flags & AP_EXPR_FLAG_STRING_RESULT) {
-        BEGIN(str);
+        STATE_PUSH(str, 1);
         return T_EXPR_STRING;
     }
     else {
@@ -100,7 +156,7 @@
  /*
   * Whitespaces
   */
-[ \t\n]+ { 
+<INITIAL,varext,split,join>{SPACE}+ { 
     /* NOP */
 }
 
@@ -107,267 +163,318 @@
  /*
   * strings ("..." and '...')
   */
-["'] {
-    str_ptr = str_buf;
+<INITIAL,varext,join>{QUOTE} {
+    STATE_PUSH(str, 1);
     str_del = yytext[0];
-    BEGIN(str);
     return T_STR_BEGIN;
 }
-<str>["'] {
+
+<str>{QUOTE} {
     if (yytext[0] == str_del) {
-        if (YY_START == var) {
-            PERROR("Unterminated variable in string");
-        }
-        else if (str_ptr == str_buf) {
-            BEGIN(INITIAL);
-            return T_STR_END;
-        }
-        else {
+        if (!STR_EMPTY()) {
             /* return what we have so far and scan delimiter again */
-            *str_ptr = '\0';
-            yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
             yyless(0);
-            str_ptr = str_buf;
+            yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
             return T_STRING;
         }
+        STATE_POP(1);
+        return T_STR_END;
     }
-    else {
-        STR_APPEND(yytext[0]);
-    }
+    STR_APPEND(yytext[0]);
 }
-<str,var,vararg>\n {
-    PERROR("Unterminated string or variable");
-}
-<var,vararg><<EOF>> {
-    PERROR("Unterminated string or variable");
-}
-<str><<EOF>> {
-    if (!(yyextra->flags & AP_EXPR_FLAG_STRING_RESULT)) {
-        PERROR("Unterminated string or variable");
-    }
-    else {
-        *str_ptr = '\0';
-        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
-        str_ptr = str_buf;
-        BEGIN(INITIAL);
-        return T_STRING;
-    }
-}
 
-<str,vararg>\\[0-7]{1,3} {
-    int result;
-
-    (void)sscanf(yytext+1, "%o", &result);
-    if (result > 0xff) {
-        PERROR("Escape sequence out of bound");
-    }
-    else {
-        STR_APPEND(result);
-    }
-}
-<str,vararg>\\[0-9]+ {
-    PERROR("Bad escape sequence");
-}
-<str,vararg>\\n      { STR_APPEND('\n'); }
-<str,vararg>\\r      { STR_APPEND('\r'); }
-<str,vararg>\\t      { STR_APPEND('\t'); }
-<str,vararg>\\b      { STR_APPEND('\b'); }
-<str,vararg>\\f      { STR_APPEND('\f'); }
-<str,vararg>\\(.|\n) { STR_APPEND(yytext[1]); }
-
  /* regexp backref inside string/arg */
-<str,vararg>[$][0-9] {
-    if (str_ptr != str_buf) {
+<str,vararg,regsub>{REG_REF} {
+    if (!STR_EMPTY()) {
         /* return what we have so far and scan '$x' again */
-        *str_ptr = '\0';
-        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
-        str_ptr = str_buf;
         yyless(0);
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
         return T_STRING;
     }
-    else {
-        yylval->num = yytext[1] - '0';
-        return T_REGEX_BACKREF;
-    }
+    yylval->num = yytext[1] - '0';
+    return T_REG_REF;
 }
 
-<str,vararg>[^\\\n"'%}$]+ {
-    char *cp = yytext;
-    while (*cp != '\0') {
-        STR_APPEND(*cp);
-        cp++;
-    }
-}
-
  /* variable inside string/arg */
-<str,vararg>%\{ {
-    if (str_ptr != str_buf) {
+<str,vararg,regsub>{VAR_BEGIN} {
+    if (!STR_EMPTY()) {
         /* return what we have so far and scan '%{' again */
-        *str_ptr = '\0';
-        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
         yyless(0);
-        str_ptr = str_buf;
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
         return T_STRING;
     }
-    else {
-        yy_push_state(var, yyscanner);
-        return T_VAR_BEGIN;
-    }
+    STATE_PUSH(var, 1);
+    return T_VAR_BEGIN;
 }
 
-<vararg>[%$] {
-     STR_APPEND(yytext[0]);
+ /* Any non-octal or octal higher than 377 (decimal 255) is invalid */
+<str,vararg,regsub>\\([4-9][0-9]{2}|[8-9][0-9]{0,2}) {
+    PERROR("Bad character escape sequence");
 }
+<str,vararg,regsub>\\[0-7]{1,3} {
+    int result;
+    (void)sscanf(yytext+1, "%o", &result);
+    STR_APPEND(result);
+}
+<str,vararg,regsub>\\x[0-9A-Fa-f]{1,2} {
+    int result;
+    (void)sscanf(yytext+1, "%x", &result);
+    STR_APPEND(result);
+}
+<str,vararg,regsub>\\n      { STR_APPEND('\n'); }
+<str,vararg,regsub>\\r      { STR_APPEND('\r'); }
+<str,vararg,regsub>\\t      { STR_APPEND('\t'); }
+<str,vararg,regsub>\\b      { STR_APPEND('\b'); }
+<str,vararg,regsub>\\f      { STR_APPEND('\f'); }
+<str,vararg,regsub>\\(.|\n) { STR_APPEND(yytext[1]); }
 
-<str>[%}$] {
-     STR_APPEND(yytext[0]);
+<str,vararg,regsub>\n {
+    PERROR("Unterminated string or variable");
 }
 
-%\{ {
-    yy_push_state(var, yyscanner);
-    return T_VAR_BEGIN;
+<str>. {
+    STR_APPEND(yytext[0]);
 }
+<str><<EOF>> {
+    STATE_POP(0);
+    if (YY_START != INITIAL) {
+        PERROR("Unterminated string");
+    }
+    if (!STR_EMPTY()) {
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
+    }
+    else {
+        yylval->cpVal = NULL;
+    }
+    ap_expr_parser_stack_pop(yyextra);
+    return T_STRING;
+}
 
-[$][0-9] {
+<INITIAL,varext,join>{REG_REF} {
     yylval->num = yytext[1] - '0';
-    return T_REGEX_BACKREF;
+    return T_REG_REF;
 }
 
- /*
-  * fixed name variable expansion %{XXX} and function call in %{func:arg} syntax
-  */
-<var>[a-zA-Z][a-zA-Z0-9_]* {
+<INITIAL,varext,join>{VAR_BEGIN} {
+    STATE_PUSH(var, 1);
+    return T_VAR_BEGIN;
+}
+
+<var>{TOKEN} {
+    /*
+     * fixed name variable expansion %{XXX} and function call
+     * in %{func:arg} syntax
+     */
+    str_state = 1;
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
     return T_ID;
 }
 
-<var>\} {
-    yy_pop_state(yyscanner);
-    return T_VAR_END;
+<var>{VAR_SEP} {
+    /* If we have a T_ID it's a vararg, otherwise a varext */
+    if (str_state) {
+        STATE_PUSH(vararg, 0);
+    }
+    else {
+        STATE_PUSH(varext, 0);
+    }
+    return yytext[0];
 }
 
-<var>: {
-    BEGIN(vararg);
+<varext>{VAR_SEP} {
+    STATE_POP(0);
     return yytext[0];
 }
 
-<var>.|\n {
+<var>{VAR_END} {
+    STATE_POP(1);
+    return T_VAR_END;
+}
+
+<var>(.|\n) {
     char *msg = apr_psprintf(yyextra->pool,
-                             "Invalid character in variable name '%c'", yytext[0]);
+                             "Invalid character in variable name '%c'",
+                             yytext[0]);
     PERROR(msg);
 }
 
-<vararg>\} {
-    if (str_ptr != str_buf) {
+<vararg>{VAR_END} {
+    yyless(0);
+    if (!STR_EMPTY()) {
         /* return what we have so far and scan '}' again */
-        *str_ptr = '\0';
-        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
-        str_ptr = str_buf;
-        yyless(0);
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
         return T_STRING;
     }
-    else {
-        yy_pop_state(yyscanner);
-        return T_VAR_END;
-    }
+    STATE_POP(0);
 }
+<vararg>. {
+    STR_APPEND(yytext[0]);
+}
 
+<var,vararg,varext><<EOF>> {
+    PERROR("Unterminated variable");
+}
+
  /*
   * Regular Expression
   */
-"m"[/#$%^,;:_\?\|\^\-\!\.\'\"] {
-    regex_del = yytext[1];
-    regex_ptr = regex_buf;
-    BEGIN(regex);
+<INITIAL,varext>[/] {
+    STATE_PUSH(regex, 1);
+    str_state = 'm';
+    str_del = yytext[0];
+    return T_REGEX;
 }
-"/" {
-    regex_del = yytext[0];
-    regex_ptr = regex_buf;
-    BEGIN(regex);
+<INITIAL,varext>[ms]{REG_SEP} {
+    STATE_PUSH(regex, 1);
+    str_state = yytext[0];
+    str_del = yytext[1];
+    return (str_state == 'm') ? T_REGEX : T_REGSUB;
 }
-<regex>.|\n {
-    if (yytext[0] == regex_del) {
-        *regex_ptr = '\0';
-        BEGIN(regex_flags);
+<regex>(.|\n) {
+    if (yytext[0] == str_del) {
+        STATE_POP(0);
+        STATE_PUSH(regflags, 0);
+        if (str_state != 'm') {
+            STATE_PUSH(regsub, 0);
+        }
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
+        return T_REG_MATCH;
     }
+    STR_APPEND(yytext[0]);
+}
+<regsub>. {
+    if (yytext[0] == str_del) {
+        /* Back to regflags */
+        STATE_POP(0);
+    }
     else {
-        *regex_ptr++ = yytext[0];
-        if (regex_ptr >= regex_buf + sizeof(regex_buf))
-            PERROR("Regexp too long");
+        STR_APPEND(yytext[0]);
     }
 }
-<regex_flags>i {
-    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
-    BEGIN(INITIAL);
-    return T_REGEX_I;
+<regsub><<EOF>> {
+    PERROR("Unterminated regexp");
 }
-<regex_flags>.|\n {
-    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
-    yyless(0);
-    BEGIN(INITIAL);
-    return T_REGEX;
+<regflags>. {
+    if (ap_strchr_c("ismg", yytext[0])) {
+        STR_APPEND(yytext[0]);
+    }
+    else if (apr_isalnum(yytext[0])) {
+        PERROR("Invalid regexp flag(s)");
+    }
+    else {
+        yyless(0);
+        if (!STR_EMPTY()) {
+            yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
+        }
+        else {
+            yylval->cpVal = NULL;
+            STATE_POP(1);
+        }
+        return T_REG_FLAGS;
+    }
 }
-<regex_flags><<EOF>> {
-    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
-    BEGIN(INITIAL);
-    return T_REGEX;
+<regflags><<EOF>> {
+    STATE_POP(1);
+    yylval->cpVal = NULL;
+    return T_REG_FLAGS;
 }
+<regex><<EOF>> {
+    STATE_POP(1);
+    if (YY_START != INITIAL) {
+        PERROR("Unterminated regexp");
+    }
+    yylval->cpVal = NULL;
+    return T_REG_FLAGS;
+}
 
  /*
   * Operators
   */
-==?   { return T_OP_STR_EQ; }
-"!="  { return T_OP_STR_NE; }
-"<"   { return T_OP_STR_LT; }
-"<="  { return T_OP_STR_LE; }
-">"   { return T_OP_STR_GT; }
-">="  { return T_OP_STR_GE; }
-"=~"  { return T_OP_REG; }
-"!~"  { return T_OP_NRE; }
-"and" { return T_OP_AND; }
-"&&"  { return T_OP_AND; }
-"or"  { return T_OP_OR; }
-"||"  { return T_OP_OR; }
-"not" { return T_OP_NOT; }
-"!"   { return T_OP_NOT; }
-"."   { return T_OP_CONCAT; }
-"-in"  { return T_OP_IN; }
-"-eq"  { return T_OP_EQ; }
-"-ne"  { return T_OP_NE; }
-"-ge"  { return T_OP_GE; }
-"-le"  { return T_OP_LE; }
-"-gt"  { return T_OP_GT; }
-"-lt"  { return T_OP_LT; }
+<INITIAL,varext,join>==?   { return T_OP_STR_EQ; }
+<INITIAL,varext,join>"!="  { return T_OP_STR_NE; }
+<INITIAL,varext,join>"<"   { return T_OP_STR_LT; }
+<INITIAL,varext,join>"<="  { return T_OP_STR_LE; }
+<INITIAL,varext,join>">"   { return T_OP_STR_GT; }
+<INITIAL,varext,join>">="  { return T_OP_STR_GE; }
+<INITIAL,varext,join>"=~"  { return T_OP_REG; }
+<INITIAL,varext,join>"!~"  { return T_OP_NRE; }
+<INITIAL,varext,join>"and" { return T_OP_AND; }
+<INITIAL,varext,join>"&&"  { return T_OP_AND; }
+<INITIAL,varext,join>"or"  { return T_OP_OR; }
+<INITIAL,varext,join>"||"  { return T_OP_OR; }
+<INITIAL,varext,join>"not" { return T_OP_NOT; }
+<INITIAL,varext,join>"!"   { return T_OP_NOT; }
+<INITIAL,varext,join>"."   { return T_OP_CONCAT; }
+<INITIAL,varext,join>"-in"  { return T_OP_IN; }
+<INITIAL,varext,join>"-eq"  { return T_OP_EQ; }
+<INITIAL,varext,join>"-ne"  { return T_OP_NE; }
+<INITIAL,varext,join>"-ge"  { return T_OP_GE; }
+<INITIAL,varext,join>"-le"  { return T_OP_LE; }
+<INITIAL,varext,join>"-gt"  { return T_OP_GT; }
+<INITIAL,varext,join>"-lt"  { return T_OP_LT; }
 
  /* for compatibility with ssl_expr */
-"lt"  { return T_OP_LT; }
-"le"  { return T_OP_LE; }
-"gt"  { return T_OP_GT; }
-"ge"  { return T_OP_GE; }
-"ne"  { return T_OP_NE; }
-"eq"  { return T_OP_EQ; }
-"in"  { return T_OP_IN; }
+<INITIAL,varext,join>"lt"  { return T_OP_LT; }
+<INITIAL,varext,join>"le"  { return T_OP_LE; }
+<INITIAL,varext,join>"gt"  { return T_OP_GT; }
+<INITIAL,varext,join>"ge"  { return T_OP_GE; }
+<INITIAL,varext,join>"ne"  { return T_OP_NE; }
+<INITIAL,varext,join>"eq"  { return T_OP_EQ; }
+<INITIAL,varext,join>"in"  { return T_OP_IN; }
 
-"-"[a-zA-Z_] {
+<INITIAL,varext,join>"-"[a-zA-Z_][a-zA-Z_0-9]+ {
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
-    return T_OP_UNARY;
+    return T_OP_BINARY;
 }
 
-"-"[a-zA-Z_][a-zA-Z_0-9]+ {
+<INITIAL,varext,join>"-"[a-zA-Z_] {
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
-    return T_OP_BINARY;
+    return T_OP_UNARY;
 }
 
+ /* Split a string (or list) into a(nother) list */
+<INITIAL,varext,join>"split" {
+    STATE_PUSH(split, 0);
+    return T_OP_SPLIT;
+}
+<split>{REG_SEP} {
+    STATE_POP(0);
+    STATE_PUSH(regex, 1);
+    str_del = yytext[0];
+    str_state = 'S';
+}
+<split>. {
+    PERROR("Expecting split regular expression");
+}
+<split><<EOF>> {
+    PERROR("Unterminated split");
+}
+
+ /* Join a list into a string */
+<INITIAL,varext,join>"join"  {
+    STATE_PUSH(join, 1);
+    return T_OP_JOIN;
+}
+<join>[/] {
+    if (++str_state == 3) {
+        STATE_POP(1);
+    }
+    return yytext[0];
+}
+<join><<EOF>> {
+    PERROR("Unterminated join");
+}
+
  /*
   * Specials
   */
-"true"  { return T_TRUE; }
-"false" { return T_FALSE; }
+<INITIAL,varext,join>"true"  { return T_TRUE; }
+<INITIAL,varext,join>"false" { return T_FALSE; }
 
  /*
   * Digits
   */
--?[0-9]+ {
+<INITIAL,varext,join>-?[0-9]+ {
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
     return T_DIGIT;
 }
@@ -375,7 +482,7 @@
  /*
   * Identifiers
   */
-[a-zA-Z][a-zA-Z0-9_]* {
+<INITIAL,varext,join>[a-zA-Z][a-zA-Z0-9_]* {
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
     return T_ID;
 }
@@ -383,7 +490,7 @@
  /*
   * These are parts of the grammar and are returned as is
   */
-[(){},:] {
+<INITIAL,varext,join>[(){},] {
     return yytext[0];
 }
 
@@ -390,7 +497,7 @@
  /*
   * Anything else is an error
   */
-.|\n {
+<INITIAL,varext,join>.|\n {
     char *msg = apr_psprintf(yyextra->pool, "Parse error near '%c'", yytext[0]);
     PERROR(msg);
 }
Index: include/ap_regex.h
===================================================================
--- include/ap_regex.h	(revision 1783852)
+++ include/ap_regex.h	(working copy)
@@ -77,6 +77,9 @@ extern "C" {
 #define AP_REG_NOMEM 0x20    /* nomem in our code */
 #define AP_REG_DOTALL 0x40   /* perl's /s flag */
 
+#define AP_REG_NOTEMPTY 0x080  /* Empty match not valid */
+#define AP_REG_ANCHORED 0x100  /* Match at the first position */
+
 #define AP_REG_MATCH "MATCH_" /** suggested prefix for ap_regname */
 
 /* Error values: */
Index: server/util_pcre.c
===================================================================
--- server/util_pcre.c	(revision 1783852)
+++ server/util_pcre.c	(working copy)
@@ -189,6 +189,10 @@ AP_DECLARE(int) ap_regexec_len(const ap_regex_t *p
         options |= PCRE_NOTBOL;
     if ((eflags & AP_REG_NOTEOL) != 0)
         options |= PCRE_NOTEOL;
+    if ((eflags & AP_REG_NOTEMPTY) != 0)
+        options |= PCRE_NOTEMPTY;
+    if ((eflags & AP_REG_ANCHORED) != 0)
+        options |= PCRE_ANCHORED;
 
     ((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1);    /* Only has meaning after compile */
 
Index: server/util_expr_parse.h
===================================================================
--- server/util_expr_parse.h	(revision 1783852)
+++ server/util_expr_parse.h	(working copy)
@@ -46,33 +46,38 @@
      T_ID = 264,
      T_STRING = 265,
      T_REGEX = 266,
-     T_REGEX_I = 267,
-     T_REGEX_BACKREF = 268,
-     T_OP_UNARY = 269,
-     T_OP_BINARY = 270,
-     T_STR_BEGIN = 271,
-     T_STR_END = 272,
-     T_VAR_BEGIN = 273,
-     T_VAR_END = 274,
-     T_OP_EQ = 275,
-     T_OP_NE = 276,
-     T_OP_LT = 277,
-     T_OP_LE = 278,
-     T_OP_GT = 279,
-     T_OP_GE = 280,
-     T_OP_REG = 281,
-     T_OP_NRE = 282,
-     T_OP_IN = 283,
-     T_OP_STR_EQ = 284,
-     T_OP_STR_NE = 285,
-     T_OP_STR_LT = 286,
-     T_OP_STR_LE = 287,
-     T_OP_STR_GT = 288,
-     T_OP_STR_GE = 289,
-     T_OP_CONCAT = 290,
-     T_OP_OR = 291,
-     T_OP_AND = 292,
-     T_OP_NOT = 293
+     T_REGSUB = 267,
+     T_REG_MATCH = 268,
+     T_REG_SUBST = 269,
+     T_REG_FLAGS = 270,
+     T_REG_REF = 271,
+     T_OP_UNARY = 272,
+     T_OP_BINARY = 273,
+     T_STR_BEGIN = 274,
+     T_STR_END = 275,
+     T_VAR_BEGIN = 276,
+     T_VAR_END = 277,
+     T_OP_EQ = 278,
+     T_OP_NE = 279,
+     T_OP_LT = 280,
+     T_OP_LE = 281,
+     T_OP_GT = 282,
+     T_OP_GE = 283,
+     T_OP_REG = 284,
+     T_OP_NRE = 285,
+     T_OP_IN = 286,
+     T_OP_STR_EQ = 287,
+     T_OP_STR_NE = 288,
+     T_OP_STR_LT = 289,
+     T_OP_STR_LE = 290,
+     T_OP_STR_GT = 291,
+     T_OP_STR_GE = 292,
+     T_OP_CONCAT = 293,
+     T_OP_SPLIT = 294,
+     T_OP_JOIN = 295,
+     T_OP_OR = 296,
+     T_OP_AND = 297,
+     T_OP_NOT = 298
    };
 #endif
 
@@ -92,7 +97,7 @@ typedef union YYSTYPE
 
 
 /* Line 2068 of yacc.c  */
-#line 96 "util_expr_parse.h"
+#line 101 "util_expr_parse.h"
 } YYSTYPE;
 # define YYSTYPE_IS_TRIVIAL 1
 # define yystype YYSTYPE /* obsolescent; will be withdrawn */
Index: server/util_expr_parse.c
===================================================================
--- server/util_expr_parse.c	(revision 1783852)
+++ server/util_expr_parse.c	(working copy)
@@ -116,33 +116,38 @@
      T_ID = 264,
      T_STRING = 265,
      T_REGEX = 266,
-     T_REGEX_I = 267,
-     T_REGEX_BACKREF = 268,
-     T_OP_UNARY = 269,
-     T_OP_BINARY = 270,
-     T_STR_BEGIN = 271,
-     T_STR_END = 272,
-     T_VAR_BEGIN = 273,
-     T_VAR_END = 274,
-     T_OP_EQ = 275,
-     T_OP_NE = 276,
-     T_OP_LT = 277,
-     T_OP_LE = 278,
-     T_OP_GT = 279,
-     T_OP_GE = 280,
-     T_OP_REG = 281,
-     T_OP_NRE = 282,
-     T_OP_IN = 283,
-     T_OP_STR_EQ = 284,
-     T_OP_STR_NE = 285,
-     T_OP_STR_LT = 286,
-     T_OP_STR_LE = 287,
-     T_OP_STR_GT = 288,
-     T_OP_STR_GE = 289,
-     T_OP_CONCAT = 290,
-     T_OP_OR = 291,
-     T_OP_AND = 292,
-     T_OP_NOT = 293
+     T_REGSUB = 267,
+     T_REG_MATCH = 268,
+     T_REG_SUBST = 269,
+     T_REG_FLAGS = 270,
+     T_REG_REF = 271,
+     T_OP_UNARY = 272,
+     T_OP_BINARY = 273,
+     T_STR_BEGIN = 274,
+     T_STR_END = 275,
+     T_VAR_BEGIN = 276,
+     T_VAR_END = 277,
+     T_OP_EQ = 278,
+     T_OP_NE = 279,
+     T_OP_LT = 280,
+     T_OP_LE = 281,
+     T_OP_GT = 282,
+     T_OP_GE = 283,
+     T_OP_REG = 284,
+     T_OP_NRE = 285,
+     T_OP_IN = 286,
+     T_OP_STR_EQ = 287,
+     T_OP_STR_NE = 288,
+     T_OP_STR_LT = 289,
+     T_OP_STR_LE = 290,
+     T_OP_STR_GT = 291,
+     T_OP_STR_GE = 292,
+     T_OP_CONCAT = 293,
+     T_OP_SPLIT = 294,
+     T_OP_JOIN = 295,
+     T_OP_OR = 296,
+     T_OP_AND = 297,
+     T_OP_NOT = 298
    };
 #endif
 
@@ -162,7 +167,7 @@ typedef union YYSTYPE
 
 
 /* Line 293 of yacc.c  */
-#line 166 "util_expr_parse.c"
+#line 171 "util_expr_parse.c"
 } YYSTYPE;
 # define YYSTYPE_IS_TRIVIAL 1
 # define yystype YYSTYPE /* obsolescent; will be withdrawn */
@@ -173,7 +178,7 @@ typedef union YYSTYPE
 /* Copy the second part of user declarations.  */
 
 /* Line 343 of yacc.c  */
-#line 102 "util_expr_parse.y"
+#line 113 "util_expr_parse.y"
 
 #include "util_expr_private.h"
 #define yyscanner ctx->scanner
@@ -182,7 +187,7 @@ int ap_expr_yylex(YYSTYPE *lvalp, void *scanner);
 
 
 /* Line 343 of yacc.c  */
-#line 186 "util_expr_parse.c"
+#line 191 "util_expr_parse.c"
 
 #ifdef short
 # undef short
@@ -399,22 +404,22 @@ union yyalloc
 #endif /* !YYCOPY_NEEDED */
 
 /* YYFINAL -- State number of the termination state.  */
-#define YYFINAL  28
+#define YYFINAL  29
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   128
+#define YYLAST   400
 
 /* YYNTOKENS -- Number of terminals.  */
-#define YYNTOKENS  45
+#define YYNTOKENS  51
 /* YYNNTS -- Number of nonterminals.  */
-#define YYNNTS  14
+#define YYNNTS  17
 /* YYNRULES -- Number of rules.  */
-#define YYNRULES  53
+#define YYNRULES  66
 /* YYNRULES -- Number of states.  */
-#define YYNSTATES  96
+#define YYNSTATES  132
 
 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
 #define YYUNDEFTOK  2
-#define YYMAXUTOK   293
+#define YYMAXUTOK   298
 
 #define YYTRANSLATE(YYX)						\
   ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
@@ -426,8 +431,8 @@ static const yytype_uint8 yytranslate[] =
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-      39,    40,     2,     2,    43,     2,     2,     2,     2,     2,
-       2,     2,     2,     2,     2,     2,     2,     2,    44,     2,
+      44,    45,     2,     2,    48,     2,     2,    50,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,    49,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -434,7 +439,7 @@ static const yytype_uint8 yytranslate[] =
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-       2,     2,     2,    41,     2,    42,     2,     2,     2,     2,
+       2,     2,     2,    46,     2,    47,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
        2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -451,7 +456,7 @@ static const yytype_uint8 yytranslate[] =
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
       15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
       25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
-      35,    36,    37,    38
+      35,    36,    37,    38,    39,    40,    41,    42,    43
 };
 
 #if YYDEBUG
@@ -459,46 +464,53 @@ static const yytype_uint8 yytranslate[] =
    YYRHS.  */
 static const yytype_uint8 yyprhs[] =
 {
-       0,     0,     3,     6,     9,    11,    13,    15,    18,    22,
-      26,    28,    31,    35,    39,    41,    45,    49,    53,    57,
-      61,    65,    69,    73,    77,    81,    85,    89,    93,    97,
-     101,   103,   107,   109,   113,   116,   118,   120,   122,   124,
-     126,   130,   136,   138,   142,   144,   146,   148,   152,   155,
-     157,   159,   161,   166
+       0,     0,     3,     6,     9,    11,    13,    15,    19,    23,
+      26,    28,    32,    34,    38,    42,    46,    50,    54,    58,
+      62,    66,    70,    74,    78,    82,    86,    90,    94,    98,
+     101,   103,   107,   111,   115,   119,   121,   125,   127,   130,
+     132,   134,   136,   138,   142,   148,   154,   158,   162,   164,
+     166,   168,   170,   176,   183,   185,   188,   192,   196,   200,
+     205,   210,   212,   214,   216,   218,   223
 };
 
 /* YYRHS -- A `-1'-separated list of the rules' RHS.  */
 static const yytype_int8 yyrhs[] =
 {
-      46,     0,    -1,     5,    47,    -1,     6,    51,    -1,     7,
-      -1,     3,    -1,     4,    -1,    38,    47,    -1,    47,    36,
-      47,    -1,    47,    37,    47,    -1,    48,    -1,    14,    54,
-      -1,    54,    15,    54,    -1,    39,    47,    40,    -1,     7,
-      -1,    54,    20,    54,    -1,    54,    21,    54,    -1,    54,
-      22,    54,    -1,    54,    23,    54,    -1,    54,    24,    54,
-      -1,    54,    25,    54,    -1,    54,    29,    54,    -1,    54,
-      30,    54,    -1,    54,    31,    54,    -1,    54,    32,    54,
-      -1,    54,    33,    54,    -1,    54,    34,    54,    -1,    54,
-      28,    49,    -1,    54,    26,    55,    -1,    54,    27,    55,
-      -1,    57,    -1,    41,    50,    42,    -1,    54,    -1,    50,
-      43,    54,    -1,    51,    52,    -1,    52,    -1,     7,    -1,
-      10,    -1,    53,    -1,    56,    -1,    18,     9,    19,    -1,
-      18,     9,    44,    51,    19,    -1,     8,    -1,    54,    35,
-      54,    -1,    53,    -1,    56,    -1,    58,    -1,    16,    51,
-      17,    -1,    16,    17,    -1,    11,    -1,    12,    -1,    13,
-      -1,     9,    39,    54,    40,    -1,     9,    39,    54,    40,
-      -1
+      52,     0,    -1,     5,    53,    -1,     6,    57,    -1,     7,
+      -1,     3,    -1,     4,    -1,    53,    41,    53,    -1,    53,
+      42,    53,    -1,    43,    53,    -1,    54,    -1,    44,    53,
+      45,    -1,     7,    -1,    60,    23,    60,    -1,    60,    24,
+      60,    -1,    60,    25,    60,    -1,    60,    26,    60,    -1,
+      60,    27,    60,    -1,    60,    28,    60,    -1,    60,    32,
+      60,    -1,    60,    33,    60,    -1,    60,    34,    60,    -1,
+      60,    35,    60,    -1,    60,    36,    60,    -1,    60,    37,
+      60,    -1,    60,    31,    55,    -1,    60,    29,    61,    -1,
+      60,    30,    61,    -1,    60,    18,    60,    -1,    17,    60,
+      -1,    66,    -1,    46,    56,    47,    -1,    60,    29,    63,
+      -1,    55,    29,    64,    -1,    44,    55,    45,    -1,    60,
+      -1,    56,    48,    60,    -1,    58,    -1,    57,    58,    -1,
+       7,    -1,    10,    -1,    59,    -1,    65,    -1,    21,     9,
+      22,    -1,    21,     9,    49,    57,    22,    -1,    21,    49,
+      60,    49,    22,    -1,    60,    38,    60,    -1,    60,    29,
+      62,    -1,    53,    -1,    59,    -1,    65,    -1,    67,    -1,
+      40,    50,    55,    50,    50,    -1,    40,    50,    55,    50,
+      60,    50,    -1,     8,    -1,    19,    20,    -1,    19,    57,
+      20,    -1,    44,    60,    45,    -1,    11,    13,    15,    -1,
+      12,    13,    57,    15,    -1,    39,    13,    57,    15,    -1,
+      61,    -1,    62,    -1,    63,    -1,    16,    -1,     9,    44,
+      60,    45,    -1,     9,    44,    60,    45,    -1
 };
 
 /* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
 static const yytype_uint8 yyrline[] =
 {
-       0,   112,   112,   113,   114,   117,   118,   119,   120,   121,
-     122,   123,   124,   125,   126,   129,   130,   131,   132,   133,
-     134,   135,   136,   137,   138,   139,   140,   141,   142,   143,
-     146,   147,   150,   151,   154,   155,   156,   159,   160,   161,
-     164,   165,   168,   169,   170,   171,   172,   173,   174,   177,
-     186,   197,   204,   207
+       0,   123,   123,   124,   125,   128,   129,   130,   131,   132,
+     133,   134,   135,   138,   139,   140,   141,   142,   143,   144,
+     145,   146,   147,   148,   149,   150,   151,   152,   153,   154,
+     157,   158,   159,   160,   161,   164,   165,   168,   169,   170,
+     173,   174,   175,   178,   179,   180,   183,   184,   185,   186,
+     187,   188,   189,   190,   191,   192,   193,   194,   196,   205,
+     214,   231,   232,   233,   236,   243,   246
 };
 #endif
 
@@ -509,15 +521,16 @@ static const char *const yytname[] =
 {
   "$end", "error", "$undefined", "T_TRUE", "T_FALSE", "T_EXPR_BOOL",
   "T_EXPR_STRING", "T_ERROR", "T_DIGIT", "T_ID", "T_STRING", "T_REGEX",
-  "T_REGEX_I", "T_REGEX_BACKREF", "T_OP_UNARY", "T_OP_BINARY",
-  "T_STR_BEGIN", "T_STR_END", "T_VAR_BEGIN", "T_VAR_END", "T_OP_EQ",
-  "T_OP_NE", "T_OP_LT", "T_OP_LE", "T_OP_GT", "T_OP_GE", "T_OP_REG",
-  "T_OP_NRE", "T_OP_IN", "T_OP_STR_EQ", "T_OP_STR_NE", "T_OP_STR_LT",
-  "T_OP_STR_LE", "T_OP_STR_GT", "T_OP_STR_GE", "T_OP_CONCAT", "T_OP_OR",
-  "T_OP_AND", "T_OP_NOT", "'('", "')'", "'{'", "'}'", "','", "':'",
-  "$accept", "root", "expr", "comparison", "wordlist", "words", "string",
-  "strpart", "var", "word", "regex", "backref", "lstfunccall",
-  "strfunccall", 0
+  "T_REGSUB", "T_REG_MATCH", "T_REG_SUBST", "T_REG_FLAGS", "T_REG_REF",
+  "T_OP_UNARY", "T_OP_BINARY", "T_STR_BEGIN", "T_STR_END", "T_VAR_BEGIN",
+  "T_VAR_END", "T_OP_EQ", "T_OP_NE", "T_OP_LT", "T_OP_LE", "T_OP_GT",
+  "T_OP_GE", "T_OP_REG", "T_OP_NRE", "T_OP_IN", "T_OP_STR_EQ",
+  "T_OP_STR_NE", "T_OP_STR_LT", "T_OP_STR_LE", "T_OP_STR_GT",
+  "T_OP_STR_GE", "T_OP_CONCAT", "T_OP_SPLIT", "T_OP_JOIN", "T_OP_OR",
+  "T_OP_AND", "T_OP_NOT", "'('", "')'", "'{'", "'}'", "','", "':'", "'/'",
+  "$accept", "root", "cond", "comp", "wordlist", "words", "string",
+  "strpart", "var", "word", "regex", "regsub", "regsplit", "reglist",
+  "regref", "lstfunccall", "strfunccall", 0
 };
 #endif
 
@@ -529,8 +542,9 @@ static const yytype_uint16 yytoknum[] =
        0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
      265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
      275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
-     285,   286,   287,   288,   289,   290,   291,   292,   293,    40,
-      41,   123,   125,    44,    58
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
+     295,   296,   297,   298,    40,    41,   123,   125,    44,    58,
+      47
 };
 # endif
 
@@ -537,23 +551,25 @@ static const yytype_uint16 yytoknum[] =
 /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
 static const yytype_uint8 yyr1[] =
 {
-       0,    45,    46,    46,    46,    47,    47,    47,    47,    47,
-      47,    47,    47,    47,    47,    48,    48,    48,    48,    48,
-      48,    48,    48,    48,    48,    48,    48,    48,    48,    48,
-      49,    49,    50,    50,    51,    51,    51,    52,    52,    52,
-      53,    53,    54,    54,    54,    54,    54,    54,    54,    55,
-      55,    56,    57,    58
+       0,    51,    52,    52,    52,    53,    53,    53,    53,    53,
+      53,    53,    53,    54,    54,    54,    54,    54,    54,    54,
+      54,    54,    54,    54,    54,    54,    54,    54,    54,    54,
+      55,    55,    55,    55,    55,    56,    56,    57,    57,    57,
+      58,    58,    58,    59,    59,    59,    60,    60,    60,    60,
+      60,    60,    60,    60,    60,    60,    60,    60,    61,    62,
+      63,    64,    64,    64,    65,    66,    67
 };
 
 /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
 static const yytype_uint8 yyr2[] =
 {
-       0,     2,     2,     2,     1,     1,     1,     2,     3,     3,
-       1,     2,     3,     3,     1,     3,     3,     3,     3,     3,
-       3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
-       1,     3,     1,     3,     2,     1,     1,     1,     1,     1,
-       3,     5,     1,     3,     1,     1,     1,     3,     2,     1,
-       1,     1,     4,     4
+       0,     2,     2,     2,     1,     1,     1,     3,     3,     2,
+       1,     3,     1,     3,     3,     3,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,     3,     3,     2,
+       1,     3,     3,     3,     3,     1,     3,     1,     2,     1,
+       1,     1,     1,     3,     5,     5,     3,     3,     1,     1,
+       1,     1,     5,     6,     1,     2,     3,     3,     3,     4,
+       4,     1,     1,     1,     1,     4,     4
 };
 
 /* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
@@ -561,91 +577,155 @@ static const yytype_uint8 yyr2[] =
    means the default is an error.  */
 static const yytype_uint8 yydefact[] =
 {
-       0,     0,     0,     4,     0,     5,     6,    14,    42,     0,
-      51,     0,     0,     0,     0,     0,     2,    10,    44,     0,
-      45,    46,    36,    37,     3,    35,    38,    39,     1,     0,
-      11,    48,     0,     0,     7,     0,     0,     0,     0,     0,
+       0,     0,     0,     4,     0,     5,     6,    12,    54,     0,
+      64,     0,     0,     0,     0,     0,     0,    48,    10,    49,
+       0,    50,    51,    39,    40,     3,    37,    41,    42,     1,
+       0,    48,    29,    55,     0,     0,     0,     0,     9,    48,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,    34,     0,    47,    40,     0,
-      13,     8,     9,    12,    15,    16,    17,    18,    19,    20,
-      49,    50,    28,    29,     0,     0,    27,    30,    21,    22,
-      23,    24,    25,    26,    43,    53,     0,     0,     0,    32,
-      41,     0,    31,     0,    52,    33
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      38,     0,    56,    43,     0,     0,     0,     0,     0,     0,
+       0,    30,    11,    57,     7,     8,    28,    13,    14,    15,
+      16,    17,    18,     0,     0,    26,    47,    27,    25,    19,
+      20,    21,    22,    23,    24,    46,    66,     0,     0,     0,
+       0,     0,     0,    35,     0,     0,     0,     0,     0,    44,
+      45,     0,    34,    31,     0,     0,    61,    62,    63,    33,
+      52,     0,    32,    58,     0,    65,    36,     0,    53,    59,
+       0,    60
 };
 
 /* YYDEFGOTO[NTERM-NUM].  */
 static const yytype_int8 yydefgoto[] =
 {
-      -1,     4,    16,    17,    76,    88,    24,    25,    18,    19,
-      72,    20,    77,    21
+      -1,     4,    31,    18,    69,   102,    25,    26,    19,    20,
+      85,    86,   118,   119,    21,    71,    22
 };
 
 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
    STATE-NUM.  */
-#define YYPACT_NINF -35
-static const yytype_int8 yypact[] =
+#define YYPACT_NINF -47
+static const yytype_int16 yypact[] =
 {
-      48,    60,    73,   -35,     7,   -35,   -35,   -35,   -35,   -34,
-     -35,    43,     8,    11,    60,    60,    86,   -35,   -35,    80,
-     -35,   -35,   -35,   -35,   108,   -35,   -35,   -35,   -35,    43,
-      25,   -35,    79,   -17,   -35,    -8,    60,    60,    43,    43,
-      43,    43,    43,    43,    43,     5,     5,     0,    43,    43,
-      43,    43,    43,    43,    43,   -35,   -27,   -35,   -35,    73,
-     -35,    86,     3,    25,    25,    25,    25,    25,    25,    25,
-     -35,   -35,   -35,   -35,    23,    43,   -35,   -35,    25,    25,
-      25,    25,    25,    25,    25,   -35,   106,    43,    85,    25,
-     -35,   -21,   -35,    43,   -35,    25
+      15,   171,    18,   -47,    17,   -47,   -47,   -47,   -47,    25,
+     -47,   171,    66,     0,     9,   171,   171,     6,   -47,   -47,
+     341,   -47,   -47,   -47,   -47,    69,   -47,   -47,   -47,   -47,
+     171,    39,   341,   -47,    36,    -9,   171,   151,   -47,    23,
+     249,   171,   171,   171,   171,   171,   171,   171,   171,   171,
+      80,    56,   151,   171,   171,   171,   171,   171,   171,   171,
+     -47,   272,   -47,   -47,    18,   222,    49,   151,   171,   -14,
+     362,   -47,   -47,   -47,    39,   -13,   341,   341,   341,   341,
+     341,   341,   341,    48,    84,   -47,   -47,   -47,    70,   341,
+     341,   341,   341,   341,   341,   341,   -47,    50,    78,   171,
+     -15,   295,   -21,   341,    -4,    34,    -4,    86,    18,   -47,
+     -47,   318,   -47,   -47,   171,    90,   -47,   -47,   -47,   -47,
+     -47,   194,   -47,   -47,    73,   -47,   341,    18,   -47,   -47,
+      99,   -47
 };
 
 /* YYPGOTO[NTERM-NUM].  */
 static const yytype_int8 yypgoto[] =
 {
-     -35,   -35,    57,   -35,   -35,   -35,    -9,   -20,    -2,    -5,
-      -4,    -1,   -35,   -35
+     -47,   -47,     3,   -47,   -36,   -47,   -10,   -22,    -2,    94,
+     -46,     7,    10,   -47,    -1,   -47,   -47
 };
 
 /* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
    positive, shift that token.  If negative, reduce the rule which
    number is the opposite.  If YYTABLE_NINF, syntax error.  */
-#define YYTABLE_NINF -1
-static const yytype_uint8 yytable[] =
+#define YYTABLE_NINF -3
+static const yytype_int16 yytable[] =
 {
-      26,    27,    58,    32,    55,    29,    30,    28,    54,    74,
-      26,    27,    55,    85,    54,    22,    70,    71,    23,    94,
-      33,    10,    26,    27,    56,    31,    13,    59,    36,    37,
-      26,    27,    60,    63,    64,    65,    66,    67,    68,    69,
-      37,    75,    73,    78,    79,    80,    81,    82,    83,    84,
-      86,     8,     9,     1,     2,     3,    10,    26,    27,    12,
-      54,    13,    87,     5,     6,     0,    55,     7,     8,     9,
-      89,    34,    35,    10,    11,     0,    12,     0,    13,     0,
-      22,     0,    91,    23,    26,    27,    10,     0,    95,    23,
-       0,    13,    10,    61,    62,    38,    57,    13,    14,    15,
-      39,    40,    41,    42,    43,    44,    45,    46,    47,    48,
-      49,    50,    51,    52,    53,    54,    23,     0,    23,    10,
-       0,    10,    36,    37,    13,    90,    13,    92,    93
+      27,    28,    34,    60,    17,    87,    -2,    83,    84,    35,
+      27,    28,    60,    63,   104,   104,    88,    29,    38,    39,
+       1,     2,     3,    27,    28,    23,   113,   114,    24,    42,
+     112,   100,    27,    28,    10,   115,   105,     5,     6,    13,
+      64,     7,     8,     9,    74,    75,    24,    41,    42,    36,
+      10,    11,    10,    12,    97,    13,    62,    13,   116,    37,
+      24,   107,    27,    28,    41,    42,    10,    83,    72,    30,
+      39,    13,   109,    23,    14,    60,    24,    15,    16,    24,
+      41,    42,    10,    24,   120,    10,    33,    13,   129,    10,
+      13,    83,    84,    99,    13,    27,    28,   108,   124,   104,
+     110,   123,    60,   127,     0,    32,    27,    28,    60,    24,
+      40,   117,     0,     0,   131,    10,   122,   130,     0,     0,
+      13,     0,    27,    28,    61,    27,    28,     0,    27,    28,
+      65,    70,     0,     0,     0,     0,     0,    76,    77,    78,
+      79,    80,    81,    82,     0,     0,    70,    89,    90,    91,
+      92,    93,    94,    95,     5,     6,     0,     0,     7,     8,
+      66,   101,   103,     0,     0,     0,     0,    10,    11,     0,
+      12,     0,    13,     0,     5,     6,     0,     0,     7,     8,
+       9,     0,     0,     0,     0,     0,     0,    10,    11,     0,
+      12,    14,    13,   111,    15,    67,     0,    68,     0,   121,
+       0,     0,     0,     0,     0,     0,     0,     0,   126,     0,
+       0,    14,    43,     0,    15,    16,     0,    44,    45,    46,
+      47,    48,    49,    50,    51,    52,    53,    54,    55,    56,
+      57,    58,    59,     0,     0,     0,     0,     0,     0,     0,
+      43,     0,     0,     0,   128,    44,    45,    46,    47,    48,
+      49,    50,    51,    52,    53,    54,    55,    56,    57,    58,
+      59,     0,     0,     0,     0,     0,     0,    43,     0,     0,
+       0,    98,    44,    45,    46,    47,    48,    49,    50,    51,
+      52,    53,    54,    55,    56,    57,    58,    59,     0,     0,
+      43,     0,     0,     0,    73,    44,    45,    46,    47,    48,
+      49,    50,    51,    52,    53,    54,    55,    56,    57,    58,
+      59,     0,     0,    43,     0,     0,     0,    96,    44,    45,
+      46,    47,    48,    49,   106,    51,    52,    53,    54,    55,
+      56,    57,    58,    59,     0,     0,    43,     0,     0,     0,
+      73,    44,    45,    46,    47,    48,    49,    50,    51,    52,
+      53,    54,    55,    56,    57,    58,    59,     0,     0,    43,
+       0,     0,     0,   125,    44,    45,    46,    47,    48,    49,
+      50,    51,    52,    53,    54,    55,    56,    57,    58,    59,
+      43,     0,     0,     0,     0,    44,    45,    46,    47,    48,
+      49,   106,    51,    52,    53,    54,    55,    56,    57,    58,
+      59
 };
 
 #define yypact_value_is_default(yystate) \
-  ((yystate) == (-35))
+  ((yystate) == (-47))
 
 #define yytable_value_is_error(yytable_value) \
   YYID (0)
 
-static const yytype_int8 yycheck[] =
+static const yytype_int16 yycheck[] =
 {
-       2,     2,    19,    12,    24,    39,    11,     0,    35,     9,
-      12,    12,    32,    40,    35,     7,    11,    12,    10,    40,
-       9,    13,    24,    24,    29,    17,    18,    44,    36,    37,
-      32,    32,    40,    38,    39,    40,    41,    42,    43,    44,
-      37,    41,    46,    48,    49,    50,    51,    52,    53,    54,
-      59,     8,     9,     5,     6,     7,    13,    59,    59,    16,
-      35,    18,    39,     3,     4,    -1,    86,     7,     8,     9,
-      75,    14,    15,    13,    14,    -1,    16,    -1,    18,    -1,
-       7,    -1,    87,    10,    86,    86,    13,    -1,    93,    10,
-      -1,    18,    13,    36,    37,    15,    17,    18,    38,    39,
-      20,    21,    22,    23,    24,    25,    26,    27,    28,    29,
-      30,    31,    32,    33,    34,    35,    10,    -1,    10,    13,
-      -1,    13,    36,    37,    18,    19,    18,    42,    43
+       2,     2,    12,    25,     1,    51,     0,    11,    12,     9,
+      12,    12,    34,    22,    29,    29,    52,     0,    15,    16,
+       5,     6,     7,    25,    25,     7,    47,    48,    10,    42,
+      45,    67,    34,    34,    16,    39,    50,     3,     4,    21,
+      49,     7,     8,     9,    41,    42,    10,    41,    42,    49,
+      16,    17,    16,    19,    64,    21,    20,    21,   104,    50,
+      10,    13,    64,    64,    41,    42,    16,    11,    45,    44,
+      67,    21,    22,     7,    40,    97,    10,    43,    44,    10,
+      41,    42,    16,    10,    50,    16,    20,    21,    15,    16,
+      21,    11,    12,    44,    21,    97,    97,    13,   108,    29,
+      22,    15,   124,    13,    -1,    11,   108,   108,   130,    10,
+      16,   104,    -1,    -1,    15,    16,   106,   127,    -1,    -1,
+      21,    -1,   124,   124,    30,   127,   127,    -1,   130,   130,
+      36,    37,    -1,    -1,    -1,    -1,    -1,    43,    44,    45,
+      46,    47,    48,    49,    -1,    -1,    52,    53,    54,    55,
+      56,    57,    58,    59,     3,     4,    -1,    -1,     7,     8,
+       9,    67,    68,    -1,    -1,    -1,    -1,    16,    17,    -1,
+      19,    -1,    21,    -1,     3,     4,    -1,    -1,     7,     8,
+       9,    -1,    -1,    -1,    -1,    -1,    -1,    16,    17,    -1,
+      19,    40,    21,    99,    43,    44,    -1,    46,    -1,   105,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   114,    -1,
+      -1,    40,    18,    -1,    43,    44,    -1,    23,    24,    25,
+      26,    27,    28,    29,    30,    31,    32,    33,    34,    35,
+      36,    37,    38,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      18,    -1,    -1,    -1,    50,    23,    24,    25,    26,    27,
+      28,    29,    30,    31,    32,    33,    34,    35,    36,    37,
+      38,    -1,    -1,    -1,    -1,    -1,    -1,    18,    -1,    -1,
+      -1,    49,    23,    24,    25,    26,    27,    28,    29,    30,
+      31,    32,    33,    34,    35,    36,    37,    38,    -1,    -1,
+      18,    -1,    -1,    -1,    45,    23,    24,    25,    26,    27,
+      28,    29,    30,    31,    32,    33,    34,    35,    36,    37,
+      38,    -1,    -1,    18,    -1,    -1,    -1,    45,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    -1,    -1,    18,    -1,    -1,    -1,
+      45,    23,    24,    25,    26,    27,    28,    29,    30,    31,
+      32,    33,    34,    35,    36,    37,    38,    -1,    -1,    18,
+      -1,    -1,    -1,    45,    23,    24,    25,    26,    27,    28,
+      29,    30,    31,    32,    33,    34,    35,    36,    37,    38,
+      18,    -1,    -1,    -1,    -1,    23,    24,    25,    26,    27,
+      28,    29,    30,    31,    32,    33,    34,    35,    36,    37,
+      38
 };
 
 /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
@@ -652,16 +732,20 @@ static const yytype_int8 yypgoto[] =
    symbol of state STATE-NUM.  */
 static const yytype_uint8 yystos[] =
 {
-       0,     5,     6,     7,    46,     3,     4,     7,     8,     9,
-      13,    14,    16,    18,    38,    39,    47,    48,    53,    54,
-      56,    58,     7,    10,    51,    52,    53,    56,     0,    39,
-      54,    17,    51,     9,    47,    47,    36,    37,    15,    20,
-      21,    22,    23,    24,    25,    26,    27,    28,    29,    30,
-      31,    32,    33,    34,    35,    52,    54,    17,    19,    44,
-      40,    47,    47,    54,    54,    54,    54,    54,    54,    54,
-      11,    12,    55,    55,     9,    41,    49,    57,    54,    54,
-      54,    54,    54,    54,    54,    40,    51,    39,    50,    54,
-      19,    54,    42,    43,    40,    54
+       0,     5,     6,     7,    52,     3,     4,     7,     8,     9,
+      16,    17,    19,    21,    40,    43,    44,    53,    54,    59,
+      60,    65,    67,     7,    10,    57,    58,    59,    65,     0,
+      44,    53,    60,    20,    57,     9,    49,    50,    53,    53,
+      60,    41,    42,    18,    23,    24,    25,    26,    27,    28,
+      29,    30,    31,    32,    33,    34,    35,    36,    37,    38,
+      58,    60,    20,    22,    49,    60,     9,    44,    46,    55,
+      60,    66,    45,    45,    53,    53,    60,    60,    60,    60,
+      60,    60,    60,    11,    12,    61,    62,    61,    55,    60,
+      60,    60,    60,    60,    60,    60,    45,    57,    49,    44,
+      55,    60,    56,    60,    29,    50,    29,    13,    13,    22,
+      22,    60,    45,    47,    48,    39,    61,    62,    63,    64,
+      50,    60,    63,    15,    57,    45,    60,    13,    50,    15,
+      57,    15
 };
 
 #define yyerrok		(yyerrstatus = 0)
@@ -1503,7 +1587,7 @@ yyreduce:
         case 2:
 
 /* Line 1806 of yacc.c  */
-#line 112 "util_expr_parse.y"
+#line 123 "util_expr_parse.y"
     { ctx->expr = (yyvsp[(2) - (2)].exVal); }
     break;
 
@@ -1510,7 +1594,7 @@ yyreduce:
   case 3:
 
 /* Line 1806 of yacc.c  */
-#line 113 "util_expr_parse.y"
+#line 124 "util_expr_parse.y"
     { ctx->expr = (yyvsp[(2) - (2)].exVal); }
     break;
 
@@ -1517,7 +1601,7 @@ yyreduce:
   case 4:
 
 /* Line 1806 of yacc.c  */
-#line 114 "util_expr_parse.y"
+#line 125 "util_expr_parse.y"
     { YYABORT; }
     break;
 
@@ -1524,7 +1608,7 @@ yyreduce:
   case 5:
 
 /* Line 1806 of yacc.c  */
-#line 117 "util_expr_parse.y"
+#line 128 "util_expr_parse.y"
     { (yyval.exVal) = ap_expr_make(op_True,        NULL, NULL, ctx); }
     break;
 
@@ -1531,7 +1615,7 @@ yyreduce:
   case 6:
 
 /* Line 1806 of yacc.c  */
-#line 118 "util_expr_parse.y"
+#line 129 "util_expr_parse.y"
     { (yyval.exVal) = ap_expr_make(op_False,       NULL, NULL, ctx); }
     break;
 
@@ -1538,28 +1622,28 @@ yyreduce:
   case 7:
 
 /* Line 1806 of yacc.c  */
-#line 119 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_Not,         (yyvsp[(2) - (2)].exVal),   NULL, ctx); }
+#line 130 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_Or,          (yyvsp[(1) - (3)].exVal),   (yyvsp[(3) - (3)].exVal),   ctx); }
     break;
 
   case 8:
 
 /* Line 1806 of yacc.c  */
-#line 120 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_Or,          (yyvsp[(1) - (3)].exVal),   (yyvsp[(3) - (3)].exVal),   ctx); }
+#line 131 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_And,         (yyvsp[(1) - (3)].exVal),   (yyvsp[(3) - (3)].exVal),   ctx); }
     break;
 
   case 9:
 
 /* Line 1806 of yacc.c  */
-#line 121 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_And,         (yyvsp[(1) - (3)].exVal),   (yyvsp[(3) - (3)].exVal),   ctx); }
+#line 132 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_Not,         (yyvsp[(2) - (2)].exVal),   NULL, ctx); }
     break;
 
   case 10:
 
 /* Line 1806 of yacc.c  */
-#line 122 "util_expr_parse.y"
+#line 133 "util_expr_parse.y"
     { (yyval.exVal) = ap_expr_make(op_Comp,        (yyvsp[(1) - (1)].exVal),   NULL, ctx); }
     break;
 
@@ -1566,140 +1650,140 @@ yyreduce:
   case 11:
 
 /* Line 1806 of yacc.c  */
-#line 123 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_unary_op_make(       (yyvsp[(1) - (2)].cpVal),   (yyvsp[(2) - (2)].exVal),   ctx); }
+#line 134 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
     break;
 
   case 12:
 
 /* Line 1806 of yacc.c  */
-#line 124 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_binary_op_make((yyvsp[(2) - (3)].cpVal),   (yyvsp[(1) - (3)].exVal),   (yyvsp[(3) - (3)].exVal),   ctx); }
+#line 135 "util_expr_parse.y"
+    { YYABORT; }
     break;
 
   case 13:
 
 /* Line 1806 of yacc.c  */
-#line 125 "util_expr_parse.y"
-    { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
+#line 138 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_EQ,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 14:
 
 /* Line 1806 of yacc.c  */
-#line 126 "util_expr_parse.y"
-    { YYABORT; }
+#line 139 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_NE,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 15:
 
 /* Line 1806 of yacc.c  */
-#line 129 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_EQ,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 140 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_LT,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 16:
 
 /* Line 1806 of yacc.c  */
-#line 130 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_NE,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 141 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_LE,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 17:
 
 /* Line 1806 of yacc.c  */
-#line 131 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_LT,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 142 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_GT,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 18:
 
 /* Line 1806 of yacc.c  */
-#line 132 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_LE,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 143 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_GE,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 19:
 
 /* Line 1806 of yacc.c  */
-#line 133 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_GT,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 144 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_STR_EQ,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 20:
 
 /* Line 1806 of yacc.c  */
-#line 134 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_GE,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 145 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_STR_NE,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 21:
 
 /* Line 1806 of yacc.c  */
-#line 135 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_STR_EQ,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 146 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_STR_LT,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 22:
 
 /* Line 1806 of yacc.c  */
-#line 136 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_STR_NE,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 147 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_STR_LE,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 23:
 
 /* Line 1806 of yacc.c  */
-#line 137 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_STR_LT,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 148 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_STR_GT,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 24:
 
 /* Line 1806 of yacc.c  */
-#line 138 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_STR_LE,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 149 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_STR_GE,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 25:
 
 /* Line 1806 of yacc.c  */
-#line 139 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_STR_GT,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 150 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_IN,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 26:
 
 /* Line 1806 of yacc.c  */
-#line 140 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_STR_GE,  (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 151 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_REG,     (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 27:
 
 /* Line 1806 of yacc.c  */
-#line 141 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_IN,      (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 152 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_NRE,     (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 28:
 
 /* Line 1806 of yacc.c  */
-#line 142 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_REG,     (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 153 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_binary_op_make((yyvsp[(2) - (3)].cpVal), (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 29:
 
 /* Line 1806 of yacc.c  */
-#line 143 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_NRE,     (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+#line 154 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_unary_op_make(     (yyvsp[(1) - (2)].cpVal), (yyvsp[(2) - (2)].exVal), ctx); }
     break;
 
   case 30:
 
 /* Line 1806 of yacc.c  */
-#line 146 "util_expr_parse.y"
+#line 157 "util_expr_parse.y"
     { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
     break;
 
@@ -1706,7 +1790,7 @@ yyreduce:
   case 31:
 
 /* Line 1806 of yacc.c  */
-#line 147 "util_expr_parse.y"
+#line 158 "util_expr_parse.y"
     { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
     break;
 
@@ -1713,174 +1797,278 @@ yyreduce:
   case 32:
 
 /* Line 1806 of yacc.c  */
-#line 150 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_ListElement, (yyvsp[(1) - (1)].exVal), NULL, ctx); }
+#line 159 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_list_regex_make((yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 33:
 
 /* Line 1806 of yacc.c  */
-#line 151 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_ListElement, (yyvsp[(3) - (3)].exVal), (yyvsp[(1) - (3)].exVal),   ctx); }
+#line 160 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_list_regex_make((yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
     break;
 
   case 34:
 
 /* Line 1806 of yacc.c  */
-#line 154 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_Concat, (yyvsp[(1) - (2)].exVal), (yyvsp[(2) - (2)].exVal), ctx); }
+#line 161 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
     break;
 
   case 35:
 
 /* Line 1806 of yacc.c  */
-#line 155 "util_expr_parse.y"
-    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+#line 164 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_ListElement, (yyvsp[(1) - (1)].exVal), NULL, ctx); }
     break;
 
   case 36:
 
 /* Line 1806 of yacc.c  */
-#line 156 "util_expr_parse.y"
-    { YYABORT; }
+#line 165 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_ListElement, (yyvsp[(3) - (3)].exVal), (yyvsp[(1) - (3)].exVal),   ctx); }
     break;
 
   case 37:
 
 /* Line 1806 of yacc.c  */
-#line 159 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_String, (yyvsp[(1) - (1)].cpVal), NULL, ctx); }
+#line 168 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
     break;
 
   case 38:
 
 /* Line 1806 of yacc.c  */
-#line 160 "util_expr_parse.y"
-    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+#line 169 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(2) - (2)].exVal) ? ap_expr_make(op_Concat, (yyvsp[(1) - (2)].exVal), (yyvsp[(2) - (2)].exVal), ctx) : (yyvsp[(1) - (2)].exVal); }
     break;
 
   case 39:
 
 /* Line 1806 of yacc.c  */
-#line 161 "util_expr_parse.y"
-    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+#line 170 "util_expr_parse.y"
+    { YYABORT; }
     break;
 
   case 40:
 
 /* Line 1806 of yacc.c  */
-#line 164 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_var_make((yyvsp[(2) - (3)].cpVal), ctx); }
+#line 173 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].cpVal) ? ap_expr_make(op_String, (yyvsp[(1) - (1)].cpVal), NULL, ctx) : NULL; }
     break;
 
   case 41:
 
 /* Line 1806 of yacc.c  */
-#line 165 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_str_func_make((yyvsp[(2) - (5)].cpVal), (yyvsp[(4) - (5)].exVal), ctx); }
+#line 174 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
     break;
 
   case 42:
 
 /* Line 1806 of yacc.c  */
-#line 168 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_Digit,  (yyvsp[(1) - (1)].cpVal), NULL, ctx); }
+#line 175 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
     break;
 
   case 43:
 
 /* Line 1806 of yacc.c  */
-#line 169 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_Concat, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal),   ctx); }
+#line 178 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_var_make((yyvsp[(2) - (3)].cpVal), ctx); }
     break;
 
   case 44:
 
 /* Line 1806 of yacc.c  */
-#line 170 "util_expr_parse.y"
-    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+#line 179 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_str_func_make((yyvsp[(2) - (5)].cpVal), (yyvsp[(4) - (5)].exVal), ctx); }
     break;
 
   case 45:
 
 /* Line 1806 of yacc.c  */
-#line 171 "util_expr_parse.y"
-    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+#line 180 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_str_word_make((yyvsp[(3) - (5)].exVal), ctx); }
     break;
 
   case 46:
 
 /* Line 1806 of yacc.c  */
-#line 172 "util_expr_parse.y"
-    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+#line 183 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_Concat, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal),   ctx); }
     break;
 
   case 47:
 
 /* Line 1806 of yacc.c  */
-#line 173 "util_expr_parse.y"
-    { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
+#line 184 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_Regsub, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal),   ctx); }
     break;
 
   case 48:
 
 /* Line 1806 of yacc.c  */
-#line 174 "util_expr_parse.y"
-    { (yyval.exVal) = ap_expr_make(op_String, "", NULL, ctx); }
+#line 185 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_str_bool_make(  (yyvsp[(1) - (1)].exVal),       ctx); }
     break;
 
   case 49:
 
 /* Line 1806 of yacc.c  */
-#line 177 "util_expr_parse.y"
+#line 186 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+    break;
+
+  case 50:
+
+/* Line 1806 of yacc.c  */
+#line 187 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+    break;
+
+  case 51:
+
+/* Line 1806 of yacc.c  */
+#line 188 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+    break;
+
+  case 52:
+
+/* Line 1806 of yacc.c  */
+#line 189 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_Join,   (yyvsp[(3) - (5)].exVal), NULL, ctx); }
+    break;
+
+  case 53:
+
+/* Line 1806 of yacc.c  */
+#line 190 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_Join,   (yyvsp[(3) - (6)].exVal), (yyvsp[(5) - (6)].exVal),   ctx); }
+    break;
+
+  case 54:
+
+/* Line 1806 of yacc.c  */
+#line 191 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_Digit,  (yyvsp[(1) - (1)].cpVal), NULL, ctx); }
+    break;
+
+  case 55:
+
+/* Line 1806 of yacc.c  */
+#line 192 "util_expr_parse.y"
+    { (yyval.exVal) = ap_expr_make(op_String, "", NULL, ctx); }
+    break;
+
+  case 56:
+
+/* Line 1806 of yacc.c  */
+#line 193 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
+    break;
+
+  case 57:
+
+/* Line 1806 of yacc.c  */
+#line 194 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
+    break;
+
+  case 58:
+
+/* Line 1806 of yacc.c  */
+#line 196 "util_expr_parse.y"
     {
-                ap_regex_t *regex;
-                if ((regex = ap_pregcomp(ctx->pool, (yyvsp[(1) - (1)].cpVal),
-                                         AP_REG_EXTENDED|AP_REG_NOSUB)) == NULL) {
+                ap_expr_t *e = ap_expr_regex_make((yyvsp[(2) - (3)].cpVal), (yyvsp[(3) - (3)].cpVal), NULL, 0, ctx);
+                if (!e) {
                     ctx->error = "Failed to compile regular expression";
                     YYERROR;
                 }
-                (yyval.exVal) = ap_expr_make(op_Regex, regex, NULL, ctx);
+                (yyval.exVal) = e;
             }
     break;
 
-  case 50:
+  case 59:
 
 /* Line 1806 of yacc.c  */
-#line 186 "util_expr_parse.y"
+#line 205 "util_expr_parse.y"
     {
-                ap_regex_t *regex;
-                if ((regex = ap_pregcomp(ctx->pool, (yyvsp[(1) - (1)].cpVal),
-                                         AP_REG_EXTENDED|AP_REG_NOSUB|AP_REG_ICASE)) == NULL) {
+                ap_expr_t *e = ap_expr_regex_make((yyvsp[(2) - (4)].cpVal), (yyvsp[(4) - (4)].cpVal), (yyvsp[(3) - (4)].exVal), 0, ctx);
+                if (!e) {
                     ctx->error = "Failed to compile regular expression";
                     YYERROR;
                 }
-                (yyval.exVal) = ap_expr_make(op_Regex, regex, NULL, ctx);
+                (yyval.exVal) = e;
             }
     break;
 
-  case 51:
+  case 60:
 
 /* Line 1806 of yacc.c  */
-#line 197 "util_expr_parse.y"
+#line 214 "util_expr_parse.y"
     {
+                /* Returns a list:
+                 * <word> ~= split/://
+                 *  => split around ':'
+                 * <word> ~= split/:/\n/
+                 *  => split around ':', use '\n' instead where detected/stripped
+                 * <list> ~= split/.*?Ip Address:([^,]+)/$1/
+                 *  => split around the whole match, use $1 instead where detected/stipped
+                 */
+                ap_expr_t *e = ap_expr_regex_make((yyvsp[(2) - (4)].cpVal), (yyvsp[(4) - (4)].cpVal), (yyvsp[(3) - (4)].exVal), 1, ctx);
+                if (!e) {
+                    ctx->error = "Failed to compile regular expression";
+                    YYERROR;
+                }
+                (yyval.exVal) = e;
+            }
+    break;
+
+  case 61:
+
+/* Line 1806 of yacc.c  */
+#line 231 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+    break;
+
+  case 62:
+
+/* Line 1806 of yacc.c  */
+#line 232 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+    break;
+
+  case 63:
+
+/* Line 1806 of yacc.c  */
+#line 233 "util_expr_parse.y"
+    { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+    break;
+
+  case 64:
+
+/* Line 1806 of yacc.c  */
+#line 236 "util_expr_parse.y"
+    {
                 int *n = apr_palloc(ctx->pool, sizeof(int));
                 *n = (yyvsp[(1) - (1)].num);
-                (yyval.exVal) = ap_expr_make(op_RegexBackref, n, NULL, ctx);
+                (yyval.exVal) = ap_expr_make(op_Regref, n, NULL, ctx);
             }
     break;
 
-  case 52:
+  case 65:
 
 /* Line 1806 of yacc.c  */
-#line 204 "util_expr_parse.y"
+#line 243 "util_expr_parse.y"
     { (yyval.exVal) = ap_expr_list_func_make((yyvsp[(1) - (4)].cpVal), (yyvsp[(3) - (4)].exVal), ctx); }
     break;
 
-  case 53:
+  case 66:
 
 /* Line 1806 of yacc.c  */
-#line 207 "util_expr_parse.y"
+#line 246 "util_expr_parse.y"
     { (yyval.exVal) = ap_expr_str_func_make((yyvsp[(1) - (4)].cpVal), (yyvsp[(3) - (4)].exVal), ctx); }
     break;
 
@@ -1887,7 +2075,7 @@ yyreduce:
 
 
 /* Line 1806 of yacc.c  */
-#line 1891 "util_expr_parse.c"
+#line 2079 "util_expr_parse.c"
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -2118,7 +2306,7 @@ yyreturn:
 
 
 /* Line 2067 of yacc.c  */
-#line 210 "util_expr_parse.y"
+#line 249 "util_expr_parse.y"
 
 
 void yyerror(ap_expr_parse_ctx_t *ctx, const char *s)
Index: server/util_expr_scan.c
===================================================================
--- server/util_expr_scan.c	(revision 1783852)
+++ server/util_expr_scan.c	(working copy)
@@ -54,6 +54,7 @@ typedef int flex_int32_t;
 typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
 
 /* Limits of integral types. */
 #ifndef INT8_MIN
@@ -84,8 +85,6 @@ typedef unsigned int flex_uint32_t;
 #define UINT32_MAX             (4294967295U)
 #endif
 
-#endif /* ! C99 */
-
 #endif /* ! FLEXINT_H */
 
 #ifdef __cplusplus
@@ -159,15 +158,7 @@ typedef void* yyscan_t;
 
 /* Size of default input buffer. */
 #ifndef YY_BUF_SIZE
-#ifdef __ia64__
-/* On IA-64, the buffer size is 16k, not 8k.
- * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
- * Ditto for the __ia64__ case accordingly.
- */
-#define YY_BUF_SIZE 32768
-#else
 #define YY_BUF_SIZE 16384
-#endif /* __ia64__ */
 #endif
 
 /* The state buf must be large enough to hold one state per character in the main buffer.
@@ -358,8 +349,8 @@ static void yy_fatal_error (yyconst char msg[] ,yy
 	*yy_cp = '\0'; \
 	yyg->yy_c_buf_p = yy_cp;
 
-#define YY_NUM_RULES 67
-#define YY_END_OF_BUFFER 68
+#define YY_NUM_RULES 73
+#define YY_END_OF_BUFFER 74
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -367,22 +358,25 @@ struct yy_trans_info
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_accept[124] =
+static yyconst flex_int16_t yy_accept[159] =
     {   0,
         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-        0,    0,   68,   66,    1,   43,    2,   66,   66,   66,
-       65,   66,   44,   26,   63,   32,   30,   34,   64,   64,
-       64,   64,   64,   64,   64,   64,   64,   64,   64,   66,
-       14,    4,    3,   17,   17,   67,   17,   23,    4,   22,
-       20,   21,   67,   16,   16,   24,   27,   29,   28,    1,
-       31,   37,   19,   18,   39,   63,   59,   59,   59,   59,
-       59,   59,   33,   30,   36,   35,   64,   64,   57,   64,
-       55,   54,   58,   53,   52,   25,   25,   56,   64,   40,
-       64,   41,   14,   13,   15,   12,    5,    6,   10,   11,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+       74,   72,    1,   44,    2,   72,   72,   72,   71,   72,
+       45,   26,   69,   33,   31,   35,   70,   70,   70,   70,
+       70,   70,   70,   70,   70,   70,   70,   70,   70,   72,
+       16,   15,    3,   16,   16,   16,   23,   20,   19,   22,
+       25,   25,   25,   25,   24,   21,   28,   29,   29,   29,
+       29,   30,   73,   64,    1,    1,   63,   66,   70,    1,
+       32,   38,   17,   18,   40,   69,   61,   61,   61,   61,
+       61,   61,   34,   31,   37,   36,   70,   70,   58,   70,
 
-        7,    8,    9,   20,   60,   46,   48,   50,   45,   49,
-       51,   47,   38,   64,   42,   64,    5,    6,   64,   61,
-        5,   62,    0
+       56,   55,   59,   70,   54,   53,   27,   27,   57,   70,
+       41,   70,   70,   42,    4,    5,   14,    7,    7,    6,
+       12,   13,    9,   10,   11,   14,   19,   60,   47,   49,
+       51,   46,   50,   52,   48,   39,   70,   70,   43,   70,
+       70,    7,    7,    0,    6,    8,   70,   65,   70,   67,
+        7,    6,    6,    6,    8,   68,   62,    0
     } ;
 
 static yyconst flex_int32_t yy_ec[256] =
@@ -392,15 +386,15 @@ static yyconst flex_int32_t yy_ec[256] =
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    2,    4,    5,    6,    7,    8,    9,    5,   10,
        10,    1,    1,   11,   12,   13,   14,   15,   15,   15,
-       15,   15,   15,   15,   15,   16,   16,   17,    6,   18,
-       19,   20,    6,    1,   21,   21,   21,   21,   21,   21,
-       21,   21,   21,   21,   21,   21,   21,   21,   21,   21,
-       21,   21,   21,   21,   21,   21,   21,   21,   21,   21,
-        1,   22,    1,    6,   23,    1,   24,   25,   21,   26,
+       15,   16,   16,   16,   16,   17,   17,   18,    6,   19,
+       20,   21,    6,    1,   22,   22,   22,   22,   22,   22,
+       23,   23,   23,   23,   23,   23,   23,   23,   23,   23,
+       23,   23,   23,   23,   23,   23,   23,   23,   23,   23,
+        1,   24,    1,    6,   25,    1,   26,   27,   22,   28,
 
-       27,   28,   29,   21,   30,   21,   21,   31,   32,   33,
-       34,   21,   35,   36,   37,   38,   39,   21,   21,   21,
-       21,   21,   40,   41,   42,   43,    1,    1,    1,    1,
+       29,   30,   31,   23,   32,   33,   23,   34,   35,   36,
+       37,   38,   39,   40,   41,   42,   43,   23,   23,   44,
+       23,   23,   45,   46,   47,   48,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -417,129 +411,177 @@ static yyconst flex_int32_t yy_ec[256] =
         1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int32_t yy_meta[44] =
+static yyconst flex_int32_t yy_meta[49] =
     {   0,
-        1,    1,    2,    1,    2,    1,    2,    2,    1,    1,
-        1,    1,    1,    1,    3,    3,    1,    1,    1,    1,
-        3,    2,    3,    3,    3,    3,    3,    3,    3,    3,
-        3,    3,    3,    3,    3,    3,    3,    3,    3,    1,
-        1,    2,    1
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    2,    2,    2,    1,    1,    1,
+        1,    2,    3,    1,    3,    2,    2,    2,    2,    2,
+        3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
+        3,    3,    3,    3,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int16_t yy_base[133] =
+static yyconst flex_int16_t yy_base[172] =
     {   0,
-        0,    0,   41,   47,   89,    0,  130,  136,    0,    0,
-      147,  146,  175,  275,   54,   28,  275,   43,  134,  164,
-      275,  164,  275,  275,   45,  152,   32,  151,    0,  136,
-      133,  143,   26,  133,   35,  194,   38,  129,  128,  122,
-        0,  275,  275,   51,  122,  221,  275,  275,  275,  275,
-        0,  275,  275,   61,  121,  275,  275,  275,  275,   76,
-      275,  275,  275,  275,  275,   65,    0,  125,   47,  126,
-      107,  130,  275,  275,  275,  275,    0,  130,    0,  124,
-        0,    0,    0,    0,    0,  275,    0,    0,  104,    0,
-      101,  275,    0,  275,  275,  275,   71,  131,  275,  275,
+        0,    0,   46,   52,   76,    0,  122,  124,  237,  236,
+        0,    0,  130,  132,  250,  249,  170,  215,  112,  114,
+      251,  446,   59,  114,  446,   48,  205,  240,  446,  247,
+      446,  446,   51,  228,  116,  227,    0,  210,  206,  218,
+       27,  207,  205,   29,  280,  113,  201,  201,  198,  191,
+      446,  446,  446,   57,  191,  312,  446,  446,    0,  446,
+      446,  128,  190,  342,  446,  446,  446,  446,  142,  189,
+      372,  446,  446,  446,  149,  158,  446,  446,  194,  163,
+      446,  446,  446,  446,  446,  170,    0,  192,  138,  194,
+      150,  196,  446,  446,  446,  446,    0,  196,    0,  181,
 
-      275,  275,  275,    0,    0,    0,    0,    0,    0,    0,
-        0,    0,    0,   99,    0,   61,  133,  135,   57,    0,
-      138,    0,  275,  259,  262,  265,   79,   67,  268,  271,
-       65,   42
+        0,    0,    0,  182,    0,    0,  446,    0,    0,  171,
+        0,  178,  168,  446,  446,  446,  446,  174,  181,  184,
+      446,  446,  446,  446,  446,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,  150,  134,    0,  136,
+      134,  178,  187,  190,  193,    0,  112,    0,   33,    0,
+      446,  446,  446,  446,  446,    0,    0,  446,  416,  419,
+      422,  425,  428,  431,  433,  435,  438,  440,  442,   50,
+       48
     } ;
 
-static yyconst flex_int16_t yy_def[133] =
+static yyconst flex_int16_t yy_def[172] =
     {   0,
-      123,    1,  124,  124,  123,    5,  124,  124,  125,  125,
-      126,  126,  123,  123,  123,  123,  123,  123,  123,  123,
-      123,  127,  123,  123,  123,  123,  123,  123,  128,  128,
-      128,  128,  128,  128,  128,  128,  128,  128,  128,  123,
-      129,  123,  123,  123,  123,  130,  123,  123,  123,  123,
-      131,  123,  123,  123,  123,  123,  123,  123,  123,  123,
-      123,  123,  123,  123,  123,  123,  132,  132,  132,  132,
-      132,  132,  123,  123,  123,  123,  128,  128,  128,  128,
-      128,  128,  128,  128,  128,  123,  128,  128,  128,  128,
-      128,  123,  129,  123,  123,  123,  123,  123,  123,  123,
+      158,    1,  159,  159,  158,    5,  160,  160,    1,    1,
+      161,  161,  162,  162,  163,  163,  164,  164,    1,    1,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  165,
+      158,  158,  158,  158,  158,  158,  166,  166,  166,  166,
+      166,  166,  166,  166,  166,  166,  166,   45,  166,  158,
+      158,  158,  158,  158,  158,  167,  158,  158,  168,  158,
+      158,  158,  158,  167,  158,  158,  158,  158,  158,  158,
+      167,  158,  158,  158,  158,  158,  158,  158,  166,  158,
+      158,  158,  158,  158,  158,  158,  169,  169,  169,  169,
+      169,  169,  158,  158,  158,  158,  166,  166,  166,  166,
 
-      123,  123,  123,  131,  132,  132,  132,  132,  132,  132,
-      132,  132,  128,  128,  128,  128,  123,  123,  128,  128,
-      123,  128,    0,  123,  123,  123,  123,  123,  123,  123,
-      123,  123
+      166,  166,  166,  166,  166,  166,  158,  166,  166,  166,
+      166,  166,  166,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  170,  168,  169,  169,  169,
+      169,  169,  169,  169,  169,  166,  166,  166,  166,  166,
+      166,  158,  158,  158,  158,  171,  166,  166,  166,  166,
+      158,  158,  158,  158,  158,  166,  166,    0,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158
     } ;
 
-static yyconst flex_int16_t yy_nxt[319] =
+static yyconst flex_int16_t yy_nxt[495] =
     {   0,
-       14,   15,   15,   16,   17,   14,   18,   19,   20,   21,
-       21,   22,   23,   24,   25,   25,   21,   26,   27,   28,
-       29,   14,   14,   30,   29,   29,   31,   32,   33,   34,
-       35,   36,   37,   38,   29,   29,   29,   39,   29,   21,
-       40,   21,   14,   42,  105,   43,   61,   44,   45,   42,
-       74,   43,   81,   44,   45,   60,   60,   63,   63,   66,
-       66,   84,   46,   82,   88,   94,   94,  104,   46,   77,
-       62,   89,   85,  107,   75,   94,   94,   60,   60,   66,
-       66,   67,   47,  122,  108,  117,  118,  120,   47,   48,
-       48,   49,   48,   48,   48,   48,   48,   48,   48,   48,
+       22,   23,   23,   24,   25,   22,   26,   27,   28,   29,
+       29,   30,   31,   32,   33,   33,   33,   22,   34,   35,
+       36,   37,   37,   22,   22,   38,   37,   37,   39,   40,
+       41,   42,   43,   44,   45,   46,   47,   37,   37,   37,
+       48,   49,   37,   37,   29,   50,   29,   22,   52,  155,
+       53,  146,   54,   55,   52,  101,   53,  105,   54,   55,
+       80,   80,   83,   83,   83,   86,   86,   86,  102,   56,
+      106,  115,  115,  115,  157,   56,   57,   57,   57,   57,
+       57,   57,   57,   57,   57,   57,   57,   57,   57,   57,
+       57,   57,   57,   58,   57,   57,   57,   59,   59,   57,
 
-       48,   48,   48,   48,   48,   50,   48,   48,   48,   51,
-       48,   48,   51,   51,   51,   51,   51,   51,   51,   51,
-       51,   51,   51,   51,   51,   51,   51,   51,   48,   48,
-       52,   48,   42,  110,   53,  119,   54,   55,   42,  116,
-       53,  115,   54,   55,  111,  118,  118,  121,  118,  118,
-      118,   46,  118,  118,  114,  113,  112,   46,  109,  106,
-       95,   95,   92,   91,   90,   83,   80,   79,   78,   76,
-       73,   56,   65,   64,  123,   59,   59,   56,   66,   66,
-      123,  123,  123,  123,  123,  123,  123,  123,  123,  123,
-       68,  123,   69,   70,   71,  123,   72,   86,   86,   86,
+       57,   59,   59,   59,   59,   59,   59,   59,   59,   59,
+       59,   59,   59,   59,   59,   59,   59,   59,   59,   59,
+       57,   57,   60,   57,   52,   78,   52,   78,   62,   63,
+       62,   63,   52,   81,   52,   94,   69,   70,   69,   70,
+      156,  109,  115,  115,  115,   64,   37,   64,   37,  110,
+       80,   80,   79,   71,   79,   71,  115,  115,  115,   80,
+       80,   82,  150,   95,   80,   80,  130,  149,   65,  148,
+       65,   75,   76,   77,   77,   77,   77,   77,  133,  131,
+       77,   77,   77,   77,   86,   86,   86,   77,  142,  142,
+      147,  134,  151,  151,   77,  143,  143,  144,  145,  145,
 
-       86,   86,  123,  123,   86,   86,   86,   86,  123,  123,
-       86,  123,  123,  123,  123,  123,   87,  123,  123,  123,
-      123,  123,  123,  123,  123,  123,  123,  123,  123,  123,
-      123,  123,  123,  123,   86,   97,   98,  123,  123,  123,
-      123,  123,  123,  123,  123,   99,  123,  123,  100,  123,
-      123,  123,  123,  101,  123,  123,  102,  123,  103,   41,
-       41,   41,   57,   57,   57,   58,   58,   58,   93,  123,
-       93,   96,   96,   96,   13,  123,  123,  123,  123,  123,
-      123,  123,  123,  123,  123,  123,  123,  123,  123,  123,
-      123,  123,  123,  123,  123,  123,  123,  123,  123,  123,
+      145,  152,  152,  153,  153,  153,  153,  154,  154,  154,
+      141,  140,  139,  138,  137,   77,   75,   76,   77,   77,
+       77,   77,   77,  136,  135,   77,   77,   77,   77,  132,
+      129,  112,   77,  116,  116,  116,  114,  113,  112,   77,
+      111,  104,  103,  100,   99,   98,   96,   93,   85,   84,
+      158,   73,   73,   66,   66,  158,  158,  158,  158,  158,
+       77,   86,   86,   86,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,   88,  158,   89,   90,  158,
+       91,  158,   92,  107,  107,  107,  107,  107,  158,  158,
+      107,  107,  107,  107,  158,  158,  158,  107,  158,  158,
 
-      123,  123,  123,  123,  123,  123,  123,  123,  123,  123,
-      123,  123,  123,  123,  123,  123,  123,  123
+      158,  158,  158,  158,  108,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  107,  118,  119,  120,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  121,  158,
+      158,  122,  158,  158,  158,  158,  158,  123,  158,  158,
+      158,  124,  158,  125,  158,  126,  118,  119,  120,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  121,  158,
+      158,  122,  158,  158,  158,  158,  158,  123,  158,  158,
+      158,  124,  158,  125,  158,  126,  118,  119,  120,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  121,  158,
+
+      158,  122,  158,  158,  158,  158,  158,  123,  158,  158,
+      158,  124,  158,  125,  158,  126,   51,   51,   51,   61,
+       61,   61,   67,   67,   67,   68,   68,   68,   72,   72,
+       72,   74,   74,   74,   87,   87,   97,   97,  117,  117,
+      117,  127,  127,  128,  128,   21,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158
+
     } ;
 
-static yyconst flex_int16_t yy_chk[319] =
+static yyconst flex_int16_t yy_chk[495] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    3,  132,    3,   16,    3,    3,    4,
-       27,    4,   33,    4,    4,   15,   15,   18,   18,   25,
-       25,   35,    3,   33,   37,   44,   44,  131,    4,  128,
-       16,   37,   35,   69,   27,   54,   54,   60,   60,   66,
-       66,  127,    3,  119,   69,   97,   97,  116,    4,    5,
+        1,    1,    1,    1,    1,    1,    1,    1,    3,  171,
+        3,  170,    3,    3,    4,   41,    4,   44,    4,    4,
+       23,   23,   26,   26,   26,   33,   33,   33,   41,    3,
+       44,   54,   54,   54,  149,    4,    5,    5,    5,    5,
         5,    5,    5,    5,    5,    5,    5,    5,    5,    5,
+        5,    5,    5,    5,    5,    5,    5,    5,    5,    5,
 
         5,    5,    5,    5,    5,    5,    5,    5,    5,    5,
         5,    5,    5,    5,    5,    5,    5,    5,    5,    5,
-        5,    5,    5,    5,    5,    5,    5,    5,    5,    5,
-        5,    5,    7,   71,    7,  114,    7,    7,    8,   91,
-        8,   89,    8,    8,   71,   98,   98,  117,  117,  118,
-      118,    7,  121,  121,   80,   78,   72,    8,   70,   68,
-       55,   45,   40,   39,   38,   34,   32,   31,   30,   28,
-       26,    7,   20,   19,   13,   12,   11,    8,   22,   22,
-        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-       22,    0,   22,   22,   22,    0,   22,   36,   36,   36,
+        5,    5,    5,    5,    7,   19,    8,   20,    7,    7,
+        8,    8,   13,   24,   14,   35,   13,   13,   14,   14,
+      147,   46,   62,   62,   62,    7,   19,    8,   20,   46,
+       75,   75,   19,   13,   20,   14,   69,   69,   69,   76,
+       76,   24,  141,   35,   80,   80,   89,  140,    7,  138,
+        8,   17,   17,   17,   17,   17,   17,   17,   91,   89,
+       17,   17,   17,   17,   86,   86,   86,   17,  118,  118,
+      137,   91,  142,  142,   17,  119,  119,  119,  120,  120,
 
-       36,   36,    0,    0,   36,   36,   36,   36,    0,    0,
-       36,    0,    0,    0,    0,    0,   36,    0,    0,    0,
+      120,  143,  143,  143,  144,  144,  144,  145,  145,  145,
+      113,  112,  110,  104,  100,   17,   18,   18,   18,   18,
+       18,   18,   18,   98,   92,   18,   18,   18,   18,   90,
+       88,   79,   18,   70,   63,   55,   50,   49,   48,   18,
+       47,   43,   42,   40,   39,   38,   36,   34,   28,   27,
+       21,   16,   15,   10,    9,    0,    0,    0,    0,    0,
+       18,   30,   30,   30,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,   30,    0,   30,   30,    0,
+       30,    0,   30,   45,   45,   45,   45,   45,    0,    0,
+       45,   45,   45,   45,    0,    0,    0,   45,    0,    0,
+
+        0,    0,    0,    0,   45,    0,    0,    0,    0,    0,
         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-        0,    0,    0,    0,   36,   46,   46,    0,    0,    0,
-        0,    0,    0,    0,    0,   46,    0,    0,   46,    0,
-        0,    0,    0,   46,    0,    0,   46,    0,   46,  124,
-      124,  124,  125,  125,  125,  126,  126,  126,  129,    0,
-      129,  130,  130,  130,  123,  123,  123,  123,  123,  123,
-      123,  123,  123,  123,  123,  123,  123,  123,  123,  123,
-      123,  123,  123,  123,  123,  123,  123,  123,  123,  123,
+        0,    0,    0,    0,    0,   45,   56,   56,   56,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,   56,    0,
+        0,   56,    0,    0,    0,    0,    0,   56,    0,    0,
+        0,   56,    0,   56,    0,   56,   64,   64,   64,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,   64,    0,
+        0,   64,    0,    0,    0,    0,    0,   64,    0,    0,
+        0,   64,    0,   64,    0,   64,   71,   71,   71,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,   71,    0,
 
-      123,  123,  123,  123,  123,  123,  123,  123,  123,  123,
-      123,  123,  123,  123,  123,  123,  123,  123
+        0,   71,    0,    0,    0,    0,    0,   71,    0,    0,
+        0,   71,    0,   71,    0,   71,  159,  159,  159,  160,
+      160,  160,  161,  161,  161,  162,  162,  162,  163,  163,
+      163,  164,  164,  164,  165,  165,  166,  166,  167,  167,
+      167,  168,  168,  169,  169,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
+      158,  158,  158,  158
+
     } ;
 
 /* The intent behind this definition is that it'll catch
@@ -578,9 +620,10 @@ static yyconst flex_int32_t yy_ec[256] =
 
 
 
-#line 43 "util_expr_scan.l"
+#line 44 "util_expr_scan.l"
 #include "util_expr_private.h"
 #include "util_expr_parse.h"
+#include "apr_lib.h" /* apr_isalnum */
 
 #undef  YY_INPUT
 #define YY_INPUT(buf,result,max_size)                       \
@@ -601,10 +644,57 @@ static yyconst flex_int32_t yy_ec[256] =
 
 #define PERROR(msg) do { yyextra->error2 = msg ; return T_ERROR; } while (0)
 
-#define str_ptr     (yyextra->scan_ptr)
-#define str_buf     (yyextra->scan_buf)
-#define str_del     (yyextra->scan_del)
+#if 0
+#define STACK_PUSH() do { \
+    fprintf(stderr, "YY_STACK_PUSH()\n"); \
+    ap_expr_parser_stack_push(yyextra); \
+} while (0)
+#define STACK_POP() do { \
+    fprintf(stderr, "YY_STACK_POP()\n"); \
+    ap_expr_parser_stack_pop(yyextra); \
+} while (0)
 
+#define STATE_PUSH(st, sk) do { \
+    fprintf(stderr, "YY_STATE_PUSH(%i)\n", (st)); \
+    yy_push_state((st), yyscanner); \
+    if (sk) { \
+        STACK_PUSH(); \
+    } \
+} while (0)
+#define STATE_POP(sk) do { \
+    fprintf(stderr, "YY_STATE_POP()\n"); \
+    if (sk) { \
+        STACK_POP(); \
+    } \
+    yy_pop_state(yyscanner); \
+} while (0)
+#else
+#define STACK_PUSH() do { \
+    ap_expr_parser_stack_push(yyextra); \
+} while (0)
+#define STACK_POP() do { \
+    ap_expr_parser_stack_pop(yyextra); \
+} while (0)
+
+#define STATE_PUSH(st, sk) do { \
+    yy_push_state((st), yyscanner); \
+    if (sk) { \
+        STACK_PUSH(); \
+    } \
+} while (0)
+#define STATE_POP(sk) do { \
+    if (sk) { \
+        STACK_POP(); \
+    } \
+    yy_pop_state(yyscanner); \
+} while (0)
+#endif
+
+#define str_ptr     (yyextra->stack->scan_ptr)
+#define str_buf     (yyextra->stack->scan_buf)
+#define str_del     (yyextra->stack->scan_del)
+#define str_state   (yyextra->stack->scan_state)
+
 #define STR_APPEND(c) do {                          \
         *str_ptr++ = (c);                           \
         if (str_ptr >= str_buf + sizeof(str_buf))   \
@@ -611,14 +701,21 @@ static yyconst flex_int32_t yy_ec[256] =
             PERROR("String too long");              \
     } while (0)
 
-#line 615 "util_expr_scan.c"
+#define STR_EMPTY() (str_ptr == str_buf)
+#define STR_FINAL() (*str_ptr = '\0', str_ptr = str_buf)
 
+#line 708 "util_expr_scan.c"
+
 #define INITIAL 0
 #define str 1
 #define var 2
 #define vararg 3
-#define regex 4
-#define regex_flags 5
+#define varext 4
+#define regex 5
+#define regsub 6
+#define regflags 7
+#define split 8
+#define join 9
 
 #ifndef YY_NO_UNISTD_H
 /* Special case for "unistd.h", since it is non-ANSI. We include it way
@@ -747,12 +844,7 @@ static int input (yyscan_t yyscanner );
     
 /* Amount of stuff to slurp up with each read. */
 #ifndef YY_READ_BUF_SIZE
-#ifdef __ia64__
-/* On IA-64, the buffer size is 16k, not 8k */
-#define YY_READ_BUF_SIZE 16384
-#else
 #define YY_READ_BUF_SIZE 8192
-#endif /* __ia64__ */
 #endif
 
 /* Copy whatever the last rule matched to the standard output. */
@@ -760,7 +852,7 @@ static int input (yyscan_t yyscanner );
 /* This used to be an fputs(), but since the string might contain NUL's,
  * we now use fwrite().
  */
-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
 #endif
 
 /* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
@@ -771,7 +863,7 @@ static int input (yyscan_t yyscanner );
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		int c = '*'; \
-		size_t n; \
+		int n; \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
@@ -856,14 +948,10 @@ YY_DECL
 	register int yy_act;
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
 
-#line 78 "util_expr_scan.l"
+#line 138 "util_expr_scan.l"
 
 
-  char  regex_buf[MAX_STRING_LEN];
-  char *regex_ptr = NULL;
-  char  regex_del = '\0';
 
-
  /*
   * Set initial state for string expressions
   */
@@ -870,7 +958,7 @@ YY_DECL
   if (yyextra->at_start) {
     yyextra->at_start = 0;
     if (yyextra->flags & AP_EXPR_FLAG_STRING_RESULT) {
-        BEGIN(str);
+        STATE_PUSH(str, 1);
         return T_EXPR_STRING;
     }
     else {
@@ -882,7 +970,7 @@ YY_DECL
  /*
   * Whitespaces
   */
-#line 886 "util_expr_scan.c"
+#line 974 "util_expr_scan.c"
 
     yylval = yylval_param;
 
@@ -937,13 +1025,13 @@ yy_match:
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 124 )
+				if ( yy_current_state >= 159 )
 					yy_c = yy_meta[(unsigned int) yy_c];
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 			++yy_cp;
 			}
-		while ( yy_current_state != 123 );
+		while ( yy_current_state != 158 );
 		yy_cp = yyg->yy_last_accepting_cpos;
 		yy_current_state = yyg->yy_last_accepting_state;
 
@@ -966,7 +1054,7 @@ do_action:	/* This label is used only to access EO
 case 1:
 /* rule 1 can match eol */
 YY_RULE_SETUP
-#line 103 "util_expr_scan.l"
+#line 159 "util_expr_scan.l"
 { 
     /* NOP */
 }
@@ -976,506 +1064,589 @@ YY_RULE_SETUP
   */
 case 2:
 YY_RULE_SETUP
-#line 110 "util_expr_scan.l"
+#line 166 "util_expr_scan.l"
 {
-    str_ptr = str_buf;
+    STATE_PUSH(str, 1);
     str_del = yytext[0];
-    BEGIN(str);
     return T_STR_BEGIN;
 }
 	YY_BREAK
 case 3:
 YY_RULE_SETUP
-#line 116 "util_expr_scan.l"
+#line 172 "util_expr_scan.l"
 {
     if (yytext[0] == str_del) {
-        if (YY_START == var) {
-            PERROR("Unterminated variable in string");
-        }
-        else if (str_ptr == str_buf) {
-            BEGIN(INITIAL);
-            return T_STR_END;
-        }
-        else {
+        if (!STR_EMPTY()) {
             /* return what we have so far and scan delimiter again */
-            *str_ptr = '\0';
-            yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
             yyless(0);
-            str_ptr = str_buf;
+            yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
             return T_STRING;
         }
+        STATE_POP(1);
+        return T_STR_END;
     }
-    else {
-        STR_APPEND(yytext[0]);
-    }
+    STR_APPEND(yytext[0]);
 }
 	YY_BREAK
+/* regexp backref inside string/arg */
 case 4:
-/* rule 4 can match eol */
 YY_RULE_SETUP
-#line 138 "util_expr_scan.l"
+#line 187 "util_expr_scan.l"
 {
-    PERROR("Unterminated string or variable");
+    if (!STR_EMPTY()) {
+        /* return what we have so far and scan '$x' again */
+        yyless(0);
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
+        return T_STRING;
+    }
+    yylval->num = yytext[1] - '0';
+    return T_REG_REF;
 }
 	YY_BREAK
-case YY_STATE_EOF(var):
-case YY_STATE_EOF(vararg):
-#line 141 "util_expr_scan.l"
+/* variable inside string/arg */
+case 5:
+YY_RULE_SETUP
+#line 199 "util_expr_scan.l"
 {
-    PERROR("Unterminated string or variable");
+    if (!STR_EMPTY()) {
+        /* return what we have so far and scan '%{' again */
+        yyless(0);
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
+        return T_STRING;
+    }
+    STATE_PUSH(var, 1);
+    return T_VAR_BEGIN;
 }
 	YY_BREAK
-case YY_STATE_EOF(str):
-#line 144 "util_expr_scan.l"
+/* Any non-octal or octal higher than 377 (decimal 255) is invalid */
+case 6:
+YY_RULE_SETUP
+#line 211 "util_expr_scan.l"
 {
-    if (!(yyextra->flags & AP_EXPR_FLAG_STRING_RESULT)) {
-        PERROR("Unterminated string or variable");
-    }
-    else {
-        *str_ptr = '\0';
-        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
-        str_ptr = str_buf;
-        BEGIN(INITIAL);
-        return T_STRING;
-    }
+    PERROR("Bad character escape sequence");
 }
 	YY_BREAK
-case 5:
+case 7:
 YY_RULE_SETUP
-#line 157 "util_expr_scan.l"
+#line 214 "util_expr_scan.l"
 {
     int result;
-
     (void)sscanf(yytext+1, "%o", &result);
-    if (result > 0xff) {
-        PERROR("Escape sequence out of bound");
-    }
-    else {
-        STR_APPEND(result);
-    }
+    STR_APPEND(result);
 }
 	YY_BREAK
-case 6:
+case 8:
 YY_RULE_SETUP
-#line 168 "util_expr_scan.l"
+#line 219 "util_expr_scan.l"
 {
-    PERROR("Bad escape sequence");
+    int result;
+    (void)sscanf(yytext+1, "%x", &result);
+    STR_APPEND(result);
 }
 	YY_BREAK
-case 7:
+case 9:
 YY_RULE_SETUP
-#line 171 "util_expr_scan.l"
+#line 224 "util_expr_scan.l"
 { STR_APPEND('\n'); }
 	YY_BREAK
-case 8:
+case 10:
 YY_RULE_SETUP
-#line 172 "util_expr_scan.l"
+#line 225 "util_expr_scan.l"
 { STR_APPEND('\r'); }
 	YY_BREAK
-case 9:
+case 11:
 YY_RULE_SETUP
-#line 173 "util_expr_scan.l"
+#line 226 "util_expr_scan.l"
 { STR_APPEND('\t'); }
 	YY_BREAK
-case 10:
+case 12:
 YY_RULE_SETUP
-#line 174 "util_expr_scan.l"
+#line 227 "util_expr_scan.l"
 { STR_APPEND('\b'); }
 	YY_BREAK
-case 11:
+case 13:
 YY_RULE_SETUP
-#line 175 "util_expr_scan.l"
+#line 228 "util_expr_scan.l"
 { STR_APPEND('\f'); }
 	YY_BREAK
-case 12:
-/* rule 12 can match eol */
+case 14:
+/* rule 14 can match eol */
 YY_RULE_SETUP
-#line 176 "util_expr_scan.l"
+#line 229 "util_expr_scan.l"
 { STR_APPEND(yytext[1]); }
 	YY_BREAK
-/* regexp backref inside string/arg */
-case 13:
+case 15:
+/* rule 15 can match eol */
 YY_RULE_SETUP
-#line 179 "util_expr_scan.l"
+#line 231 "util_expr_scan.l"
 {
-    if (str_ptr != str_buf) {
-        /* return what we have so far and scan '$x' again */
-        *str_ptr = '\0';
-        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
-        str_ptr = str_buf;
-        yyless(0);
-        return T_STRING;
-    }
-    else {
-        yylval->num = yytext[1] - '0';
-        return T_REGEX_BACKREF;
-    }
+    PERROR("Unterminated string or variable");
 }
 	YY_BREAK
-case 14:
+case 16:
 YY_RULE_SETUP
-#line 194 "util_expr_scan.l"
+#line 235 "util_expr_scan.l"
 {
-    char *cp = yytext;
-    while (*cp != '\0') {
-        STR_APPEND(*cp);
-        cp++;
-    }
+    STR_APPEND(yytext[0]);
 }
 	YY_BREAK
-/* variable inside string/arg */
-case 15:
-YY_RULE_SETUP
-#line 203 "util_expr_scan.l"
+case YY_STATE_EOF(str):
+#line 238 "util_expr_scan.l"
 {
-    if (str_ptr != str_buf) {
-        /* return what we have so far and scan '%{' again */
-        *str_ptr = '\0';
-        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
-        yyless(0);
-        str_ptr = str_buf;
-        return T_STRING;
+    STATE_POP(0);
+    if (YY_START != INITIAL) {
+        PERROR("Unterminated string");
     }
+    if (!STR_EMPTY()) {
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
+    }
     else {
-        yy_push_state(var, yyscanner);
-        return T_VAR_BEGIN;
+        yylval->cpVal = NULL;
     }
+    ap_expr_parser_stack_pop(yyextra);
+    return T_STRING;
 }
 	YY_BREAK
-case 16:
-YY_RULE_SETUP
-#line 218 "util_expr_scan.l"
-{
-     STR_APPEND(yytext[0]);
-}
-	YY_BREAK
 case 17:
 YY_RULE_SETUP
-#line 222 "util_expr_scan.l"
+#line 253 "util_expr_scan.l"
 {
-     STR_APPEND(yytext[0]);
+    yylval->num = yytext[1] - '0';
+    return T_REG_REF;
 }
 	YY_BREAK
 case 18:
 YY_RULE_SETUP
-#line 226 "util_expr_scan.l"
+#line 258 "util_expr_scan.l"
 {
-    yy_push_state(var, yyscanner);
+    STATE_PUSH(var, 1);
     return T_VAR_BEGIN;
 }
 	YY_BREAK
 case 19:
 YY_RULE_SETUP
-#line 231 "util_expr_scan.l"
+#line 263 "util_expr_scan.l"
 {
-    yylval->num = yytext[1] - '0';
-    return T_REGEX_BACKREF;
+    /*
+     * fixed name variable expansion %{XXX} and function call
+     * in %{func:arg} syntax
+     */
+    str_state = 1;
+    yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
+    return T_ID;
 }
 	YY_BREAK
-/*
-  * fixed name variable expansion %{XXX} and function call in %{func:arg} syntax
-  */
 case 20:
 YY_RULE_SETUP
-#line 239 "util_expr_scan.l"
+#line 273 "util_expr_scan.l"
 {
-    yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
-    return T_ID;
+    /* If we have a T_ID it's a vararg, otherwise a varext */
+    if (str_state) {
+        STATE_PUSH(vararg, 0);
+    }
+    else {
+        STATE_PUSH(varext, 0);
+    }
+    return yytext[0];
 }
 	YY_BREAK
 case 21:
 YY_RULE_SETUP
-#line 244 "util_expr_scan.l"
+#line 284 "util_expr_scan.l"
 {
-    yy_pop_state(yyscanner);
-    return T_VAR_END;
+    STATE_POP(0);
+    return yytext[0];
 }
 	YY_BREAK
 case 22:
 YY_RULE_SETUP
-#line 249 "util_expr_scan.l"
+#line 289 "util_expr_scan.l"
 {
-    BEGIN(vararg);
-    return yytext[0];
+    STATE_POP(1);
+    return T_VAR_END;
 }
 	YY_BREAK
 case 23:
 /* rule 23 can match eol */
 YY_RULE_SETUP
-#line 254 "util_expr_scan.l"
+#line 294 "util_expr_scan.l"
 {
     char *msg = apr_psprintf(yyextra->pool,
-                             "Invalid character in variable name '%c'", yytext[0]);
+                             "Invalid character in variable name '%c'",
+                             yytext[0]);
     PERROR(msg);
 }
 	YY_BREAK
 case 24:
 YY_RULE_SETUP
-#line 260 "util_expr_scan.l"
+#line 301 "util_expr_scan.l"
 {
-    if (str_ptr != str_buf) {
+    yyless(0);
+    if (!STR_EMPTY()) {
         /* return what we have so far and scan '}' again */
-        *str_ptr = '\0';
-        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
-        str_ptr = str_buf;
-        yyless(0);
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
         return T_STRING;
     }
-    else {
-        yy_pop_state(yyscanner);
-        return T_VAR_END;
-    }
+    STATE_POP(0);
 }
 	YY_BREAK
-/*
-  * Regular Expression
-  */
 case 25:
 YY_RULE_SETUP
-#line 278 "util_expr_scan.l"
+#line 310 "util_expr_scan.l"
 {
-    regex_del = yytext[1];
-    regex_ptr = regex_buf;
-    BEGIN(regex);
+    STR_APPEND(yytext[0]);
 }
 	YY_BREAK
+case YY_STATE_EOF(var):
+case YY_STATE_EOF(vararg):
+case YY_STATE_EOF(varext):
+#line 314 "util_expr_scan.l"
+{
+    PERROR("Unterminated variable");
+}
+	YY_BREAK
+/*
+  * Regular Expression
+  */
 case 26:
 YY_RULE_SETUP
-#line 283 "util_expr_scan.l"
+#line 321 "util_expr_scan.l"
 {
-    regex_del = yytext[0];
-    regex_ptr = regex_buf;
-    BEGIN(regex);
+    STATE_PUSH(regex, 1);
+    str_state = 'm';
+    str_del = yytext[0];
+    return T_REGEX;
 }
 	YY_BREAK
 case 27:
-/* rule 27 can match eol */
 YY_RULE_SETUP
-#line 288 "util_expr_scan.l"
+#line 327 "util_expr_scan.l"
 {
-    if (yytext[0] == regex_del) {
-        *regex_ptr = '\0';
-        BEGIN(regex_flags);
-    }
-    else {
-        *regex_ptr++ = yytext[0];
-        if (regex_ptr >= regex_buf + sizeof(regex_buf))
-            PERROR("Regexp too long");
-    }
+    STATE_PUSH(regex, 1);
+    str_state = yytext[0];
+    str_del = yytext[1];
+    return (str_state == 'm') ? T_REGEX : T_REGSUB;
 }
 	YY_BREAK
 case 28:
+/* rule 28 can match eol */
 YY_RULE_SETUP
-#line 299 "util_expr_scan.l"
+#line 333 "util_expr_scan.l"
 {
-    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
-    BEGIN(INITIAL);
-    return T_REGEX_I;
+    if (yytext[0] == str_del) {
+        STATE_POP(0);
+        STATE_PUSH(regflags, 0);
+        if (str_state != 'm') {
+            STATE_PUSH(regsub, 0);
+        }
+        yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
+        return T_REG_MATCH;
+    }
+    STR_APPEND(yytext[0]);
 }
 	YY_BREAK
 case 29:
-/* rule 29 can match eol */
 YY_RULE_SETUP
-#line 304 "util_expr_scan.l"
+#line 345 "util_expr_scan.l"
 {
-    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
-    yyless(0);
-    BEGIN(INITIAL);
-    return T_REGEX;
+    if (yytext[0] == str_del) {
+        /* Back to regflags */
+        STATE_POP(0);
+    }
+    else {
+        STR_APPEND(yytext[0]);
+    }
 }
 	YY_BREAK
-case YY_STATE_EOF(regex_flags):
-#line 310 "util_expr_scan.l"
+case YY_STATE_EOF(regsub):
+#line 354 "util_expr_scan.l"
 {
-    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
-    BEGIN(INITIAL);
-    return T_REGEX;
+    PERROR("Unterminated regexp");
 }
 	YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 357 "util_expr_scan.l"
+{
+    if (ap_strchr_c("ismg", yytext[0])) {
+        STR_APPEND(yytext[0]);
+    }
+    else if (apr_isalnum(yytext[0])) {
+        PERROR("Invalid regexp flag(s)");
+    }
+    else {
+        yyless(0);
+        if (!STR_EMPTY()) {
+            yylval->cpVal = apr_pstrdup(yyextra->pool, STR_FINAL());
+        }
+        else {
+            yylval->cpVal = NULL;
+            STATE_POP(1);
+        }
+        return T_REG_FLAGS;
+    }
+}
+	YY_BREAK
+case YY_STATE_EOF(regflags):
+#line 376 "util_expr_scan.l"
+{
+    STATE_POP(1);
+    yylval->cpVal = NULL;
+    return T_REG_FLAGS;
+}
+	YY_BREAK
+case YY_STATE_EOF(regex):
+#line 381 "util_expr_scan.l"
+{
+    STATE_POP(1);
+    if (YY_START != INITIAL) {
+        PERROR("Unterminated regexp");
+    }
+    yylval->cpVal = NULL;
+    return T_REG_FLAGS;
+}
+	YY_BREAK
 /*
   * Operators
   */
-case 30:
+case 31:
 YY_RULE_SETUP
-#line 319 "util_expr_scan.l"
+#line 393 "util_expr_scan.l"
 { return T_OP_STR_EQ; }
 	YY_BREAK
-case 31:
+case 32:
 YY_RULE_SETUP
-#line 320 "util_expr_scan.l"
+#line 394 "util_expr_scan.l"
 { return T_OP_STR_NE; }
 	YY_BREAK
-case 32:
+case 33:
 YY_RULE_SETUP
-#line 321 "util_expr_scan.l"
+#line 395 "util_expr_scan.l"
 { return T_OP_STR_LT; }
 	YY_BREAK
-case 33:
+case 34:
 YY_RULE_SETUP
-#line 322 "util_expr_scan.l"
+#line 396 "util_expr_scan.l"
 { return T_OP_STR_LE; }
 	YY_BREAK
-case 34:
+case 35:
 YY_RULE_SETUP
-#line 323 "util_expr_scan.l"
+#line 397 "util_expr_scan.l"
 { return T_OP_STR_GT; }
 	YY_BREAK
-case 35:
+case 36:
 YY_RULE_SETUP
-#line 324 "util_expr_scan.l"
+#line 398 "util_expr_scan.l"
 { return T_OP_STR_GE; }
 	YY_BREAK
-case 36:
+case 37:
 YY_RULE_SETUP
-#line 325 "util_expr_scan.l"
+#line 399 "util_expr_scan.l"
 { return T_OP_REG; }
 	YY_BREAK
-case 37:
+case 38:
 YY_RULE_SETUP
-#line 326 "util_expr_scan.l"
+#line 400 "util_expr_scan.l"
 { return T_OP_NRE; }
 	YY_BREAK
-case 38:
-YY_RULE_SETUP
-#line 327 "util_expr_scan.l"
-{ return T_OP_AND; }
-	YY_BREAK
 case 39:
 YY_RULE_SETUP
-#line 328 "util_expr_scan.l"
+#line 401 "util_expr_scan.l"
 { return T_OP_AND; }
 	YY_BREAK
 case 40:
 YY_RULE_SETUP
-#line 329 "util_expr_scan.l"
-{ return T_OP_OR; }
+#line 402 "util_expr_scan.l"
+{ return T_OP_AND; }
 	YY_BREAK
 case 41:
 YY_RULE_SETUP
-#line 330 "util_expr_scan.l"
+#line 403 "util_expr_scan.l"
 { return T_OP_OR; }
 	YY_BREAK
 case 42:
 YY_RULE_SETUP
-#line 331 "util_expr_scan.l"
-{ return T_OP_NOT; }
+#line 404 "util_expr_scan.l"
+{ return T_OP_OR; }
 	YY_BREAK
 case 43:
 YY_RULE_SETUP
-#line 332 "util_expr_scan.l"
+#line 405 "util_expr_scan.l"
 { return T_OP_NOT; }
 	YY_BREAK
 case 44:
 YY_RULE_SETUP
-#line 333 "util_expr_scan.l"
-{ return T_OP_CONCAT; }
+#line 406 "util_expr_scan.l"
+{ return T_OP_NOT; }
 	YY_BREAK
 case 45:
 YY_RULE_SETUP
-#line 334 "util_expr_scan.l"
-{ return T_OP_IN; }
+#line 407 "util_expr_scan.l"
+{ return T_OP_CONCAT; }
 	YY_BREAK
 case 46:
 YY_RULE_SETUP
-#line 335 "util_expr_scan.l"
-{ return T_OP_EQ; }
+#line 408 "util_expr_scan.l"
+{ return T_OP_IN; }
 	YY_BREAK
 case 47:
 YY_RULE_SETUP
-#line 336 "util_expr_scan.l"
-{ return T_OP_NE; }
+#line 409 "util_expr_scan.l"
+{ return T_OP_EQ; }
 	YY_BREAK
 case 48:
 YY_RULE_SETUP
-#line 337 "util_expr_scan.l"
-{ return T_OP_GE; }
+#line 410 "util_expr_scan.l"
+{ return T_OP_NE; }
 	YY_BREAK
 case 49:
 YY_RULE_SETUP
-#line 338 "util_expr_scan.l"
-{ return T_OP_LE; }
+#line 411 "util_expr_scan.l"
+{ return T_OP_GE; }
 	YY_BREAK
 case 50:
 YY_RULE_SETUP
-#line 339 "util_expr_scan.l"
-{ return T_OP_GT; }
+#line 412 "util_expr_scan.l"
+{ return T_OP_LE; }
 	YY_BREAK
 case 51:
 YY_RULE_SETUP
-#line 340 "util_expr_scan.l"
-{ return T_OP_LT; }
+#line 413 "util_expr_scan.l"
+{ return T_OP_GT; }
 	YY_BREAK
-/* for compatibility with ssl_expr */
 case 52:
 YY_RULE_SETUP
-#line 343 "util_expr_scan.l"
+#line 414 "util_expr_scan.l"
 { return T_OP_LT; }
 	YY_BREAK
+/* for compatibility with ssl_expr */
 case 53:
 YY_RULE_SETUP
-#line 344 "util_expr_scan.l"
-{ return T_OP_LE; }
+#line 417 "util_expr_scan.l"
+{ return T_OP_LT; }
 	YY_BREAK
 case 54:
 YY_RULE_SETUP
-#line 345 "util_expr_scan.l"
-{ return T_OP_GT; }
+#line 418 "util_expr_scan.l"
+{ return T_OP_LE; }
 	YY_BREAK
 case 55:
 YY_RULE_SETUP
-#line 346 "util_expr_scan.l"
-{ return T_OP_GE; }
+#line 419 "util_expr_scan.l"
+{ return T_OP_GT; }
 	YY_BREAK
 case 56:
 YY_RULE_SETUP
-#line 347 "util_expr_scan.l"
-{ return T_OP_NE; }
+#line 420 "util_expr_scan.l"
+{ return T_OP_GE; }
 	YY_BREAK
 case 57:
 YY_RULE_SETUP
-#line 348 "util_expr_scan.l"
-{ return T_OP_EQ; }
+#line 421 "util_expr_scan.l"
+{ return T_OP_NE; }
 	YY_BREAK
 case 58:
 YY_RULE_SETUP
-#line 349 "util_expr_scan.l"
-{ return T_OP_IN; }
+#line 422 "util_expr_scan.l"
+{ return T_OP_EQ; }
 	YY_BREAK
 case 59:
 YY_RULE_SETUP
-#line 351 "util_expr_scan.l"
+#line 423 "util_expr_scan.l"
+{ return T_OP_IN; }
+	YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 425 "util_expr_scan.l"
 {
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
-    return T_OP_UNARY;
+    return T_OP_BINARY;
 }
 	YY_BREAK
-case 60:
+case 61:
 YY_RULE_SETUP
-#line 356 "util_expr_scan.l"
+#line 430 "util_expr_scan.l"
 {
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
-    return T_OP_BINARY;
+    return T_OP_UNARY;
 }
 	YY_BREAK
+/* Split a string (or list) into a(nother) list */
+case 62:
+YY_RULE_SETUP
+#line 436 "util_expr_scan.l"
+{
+    STATE_PUSH(split, 0);
+    return T_OP_SPLIT;
+}
+	YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 440 "util_expr_scan.l"
+{
+    STATE_POP(0);
+    STATE_PUSH(regex, 1);
+    str_del = yytext[0];
+    str_state = 'S';
+}
+	YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 446 "util_expr_scan.l"
+{
+    PERROR("Expecting split regular expression");
+}
+	YY_BREAK
+case YY_STATE_EOF(split):
+#line 449 "util_expr_scan.l"
+{
+    PERROR("Unterminated split");
+}
+	YY_BREAK
+/* Join a list into a string */
+case 65:
+YY_RULE_SETUP
+#line 454 "util_expr_scan.l"
+{
+    STATE_PUSH(join, 1);
+    return T_OP_JOIN;
+}
+	YY_BREAK
+case 66:
+YY_RULE_SETUP
+#line 458 "util_expr_scan.l"
+{
+    if (++str_state == 3) {
+        STATE_POP(1);
+    }
+    return yytext[0];
+}
+	YY_BREAK
+case YY_STATE_EOF(join):
+#line 464 "util_expr_scan.l"
+{
+    PERROR("Unterminated join");
+}
+	YY_BREAK
 /*
   * Specials
   */
-case 61:
+case 67:
 YY_RULE_SETUP
-#line 364 "util_expr_scan.l"
+#line 471 "util_expr_scan.l"
 { return T_TRUE; }
 	YY_BREAK
-case 62:
+case 68:
 YY_RULE_SETUP
-#line 365 "util_expr_scan.l"
+#line 472 "util_expr_scan.l"
 { return T_FALSE; }
 	YY_BREAK
 /*
   * Digits
   */
-case 63:
+case 69:
 YY_RULE_SETUP
-#line 370 "util_expr_scan.l"
+#line 477 "util_expr_scan.l"
 {
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
     return T_DIGIT;
@@ -1484,9 +1655,9 @@ YY_RULE_SETUP
 /*
   * Identifiers
   */
-case 64:
+case 70:
 YY_RULE_SETUP
-#line 378 "util_expr_scan.l"
+#line 485 "util_expr_scan.l"
 {
     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
     return T_ID;
@@ -1495,9 +1666,9 @@ YY_RULE_SETUP
 /*
   * These are parts of the grammar and are returned as is
   */
-case 65:
+case 71:
 YY_RULE_SETUP
-#line 386 "util_expr_scan.l"
+#line 493 "util_expr_scan.l"
 {
     return yytext[0];
 }
@@ -1505,23 +1676,22 @@ YY_RULE_SETUP
 /*
   * Anything else is an error
   */
-case 66:
-/* rule 66 can match eol */
+case 72:
+/* rule 72 can match eol */
 YY_RULE_SETUP
-#line 393 "util_expr_scan.l"
+#line 500 "util_expr_scan.l"
 {
     char *msg = apr_psprintf(yyextra->pool, "Parse error near '%c'", yytext[0]);
     PERROR(msg);
 }
 	YY_BREAK
-case 67:
+case 73:
 YY_RULE_SETUP
-#line 398 "util_expr_scan.l"
+#line 505 "util_expr_scan.l"
 YY_FATAL_ERROR( "flex scanner jammed" );
 	YY_BREAK
-#line 1523 "util_expr_scan.c"
+#line 1694 "util_expr_scan.c"
 case YY_STATE_EOF(INITIAL):
-case YY_STATE_EOF(regex):
 	yyterminate();
 
 	case YY_END_OF_BUFFER:
@@ -1815,7 +1985,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 124 )
+			if ( yy_current_state >= 159 )
 				yy_c = yy_meta[(unsigned int) yy_c];
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1844,11 +2014,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 124 )
+		if ( yy_current_state >= 159 )
 			yy_c = yy_meta[(unsigned int) yy_c];
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-	yy_is_jam = (yy_current_state == 123);
+	yy_is_jam = (yy_current_state == 158);
 
 	return yy_is_jam ? 0 : yy_current_state;
 }
@@ -2251,8 +2421,8 @@ YY_BUFFER_STATE ap_expr_yy_scan_string (yyconst ch
 
 /** Setup the input buffer state to scan the given bytes. The next call to ap_expr_yylex() will
  * scan from a @e copy of @a bytes.
- * @param yybytes the byte buffer to scan
- * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
  * @param yyscanner The scanner object.
  * @return the newly allocated buffer state object.
  */
@@ -2661,7 +2831,7 @@ void ap_expr_yyfree (void * ptr , yyscan_t yyscann
 
 #define YYTABLES_NAME "yytables"
 
-#line 398 "util_expr_scan.l"
+#line 505 "util_expr_scan.l"
 
 
 
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@xxxxxxxxxxxxxxxx
For additional commands, e-mail: users-help@xxxxxxxxxxxxxxxx

[Index of Archives]     [Open SSH Users]     [Linux ACPI]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Squid]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]

  Powered by Linux