From 0d02808d1ec43bb8f9f5426dbb7c4a63ab60c51e Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Wed, 1 Nov 2023 12:40:44 +0100 Subject: [PATCH] Revert all except head/tail --- cmake/NeuronFileLists.cmake | 2 +- src/ivos/InterViews/_defines.h | 1 + src/ivos/InterViews/_undefs.h | 1 + src/ivos/InterViews/regexp.h | 80 +++ src/ivos/regexp.cpp | 1219 ++++++++++++++++++++++++++++++++ src/nrncvode/netcvode.cpp | 129 ++-- 6 files changed, 1389 insertions(+), 43 deletions(-) create mode 100755 src/ivos/InterViews/regexp.h create mode 100644 src/ivos/regexp.cpp diff --git a/cmake/NeuronFileLists.cmake b/cmake/NeuronFileLists.cmake index e98f4860c2..2b6ee3d1ee 100644 --- a/cmake/NeuronFileLists.cmake +++ b/cmake/NeuronFileLists.cmake @@ -427,7 +427,7 @@ set(NMODL_FILES_LIST units.cpp version.cpp) -set(IVOS_FILES_LIST listimpl.cpp observe.cpp resource.cpp) +set(IVOS_FILES_LIST listimpl.cpp observe.cpp regexp.cpp resource.cpp) set(MPI_DYNAMIC_INCLUDE nrnmpi_dynam.h nrnmpi_dynam_cinc nrnmpi_dynam_wrappers.inc) diff --git a/src/ivos/InterViews/_defines.h b/src/ivos/InterViews/_defines.h index 9b2a95fa6f..7bb9234e5c 100755 --- a/src/ivos/InterViews/_defines.h +++ b/src/ivos/InterViews/_defines.h @@ -217,6 +217,7 @@ #define Raster _lib_iv(Raster) #define RasterRep _lib_iv(RasterRep) #define Reducer _lib_iv(Reducer) +#define Regexp _lib_iv(Regexp) #define ReqErr _lib_iv(ReqErr) #define Requirement _lib_iv(Requirement) #define Requisition _lib_iv(Requisition) diff --git a/src/ivos/InterViews/_undefs.h b/src/ivos/InterViews/_undefs.h index 6891baa495..a93e162890 100755 --- a/src/ivos/InterViews/_undefs.h +++ b/src/ivos/InterViews/_undefs.h @@ -217,6 +217,7 @@ #undef Raster #undef RasterRep #undef Reducer +#undef Regexp #undef ReqErr #undef Requirement #undef Requisition diff --git a/src/ivos/InterViews/regexp.h b/src/ivos/InterViews/regexp.h new file mode 100755 index 0000000000..c1466fd7dc --- /dev/null +++ b/src/ivos/InterViews/regexp.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1987, 1988, 1989, 1990, 1991 Stanford University + * Copyright (c) 1991 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Stanford and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Stanford and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * Regexp - regular expression searching + */ + +#ifndef iv_regexp_h +#define iv_regexp_h + +#include + +/* + * These definitions are from Henry Spencers public-domain regular + * expression matching routines. + * + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + */ +#define NSUBEXP 10 +struct regexp { + char *startp[NSUBEXP]; + char *endp[NSUBEXP]; + char *textStart; + char regstart; /* Internal use only. */ + char reganch; /* Internal use only. */ + char *regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + char program[1]; /* Unwarranted chumminess with compiler. */ +}; + +/* + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. This used to be the octal + * integer literal 0234 = 156, which would be implicitly converted to -100 when + * narrowing to signed 8 bit char. This conversion was implementation defined + * before C++20. + */ +#define REGEXP_MAGIC static_cast(-100) + +class Regexp { +public: + Regexp(const char*); + Regexp(const char*, int length); + ~Regexp(); + + const char* pattern() const; + int Search(const char* text, int length, int index, int range); + int Match(const char* text, int length, int index); + int BeginningOfMatch(int subexp = 0); + int EndOfMatch(int subexp = 0); +private: + char* pattern_; + regexp* c_pattern; +}; + +#endif diff --git a/src/ivos/regexp.cpp b/src/ivos/regexp.cpp new file mode 100644 index 0000000000..ed96c43bcf --- /dev/null +++ b/src/ivos/regexp.cpp @@ -0,0 +1,1219 @@ +#ifdef HAVE_CONFIG_H +#include <../../nrnconf.h> +#endif +/* + * Copyright (c) 1987, 1988, 1989, 1990, 1991 Stanford University + * Copyright (c) 1991 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Stanford and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Stanford and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * Regexp - regular expression searching + */ + +#include +#include +#include + +/* + * This version is based on the Henry Spencers public domain reimplementation + * of the regular expression matching subroutines. They are included as + * static subroutines after the externally accessible routines. + */ + +/* + * Forward declarations for regcomp()'s friends. + */ +static regexp* regcomp(const char* exp); +static char* reg(int paren, int* flagp); +static char* regbranch(int* flagp); +static char* regpiece(int* flagp); +static char* regatom(int* flagp); +static char* regnode(char op); +static char* regnext(char* p); +static void regc(char b); +static void reginsert(char op, char* opnd); +static void regtail(char* p, char* val); +static void regoptail(char* p, char* val); +static void regerror(const char* s); +static int regexec(regexp* prog, char* string); +static int regtry(regexp* prog, char* string); +static int regmatch(char* prog); +static int regrepeat(char* p); + + +inline char * +FindNewline(char* s) { + return strchr(s, '\n'); +} + +inline char * +NextLine(char* s) { + char* newstart; + + if ((newstart = FindNewline(s)) != nil) + newstart++; + return newstart; +} + +Regexp::Regexp (const char* pat) { + int length = strlen(pat); + pattern_ = new char[length+1]; + strncpy(pattern_, pat, length); + pattern_[length] = '\0'; + c_pattern = regcomp(pattern_); + if (!c_pattern) { + delete [] pattern_; + pattern_ = nil; + } +} + +Regexp::Regexp (const char* pat, int length) { + pattern_ = new char[length+1]; + strncpy(pattern_, pat, length); + pattern_[length] = '\0'; + c_pattern = regcomp(pattern_); + if (!c_pattern) { + delete [] pattern_; + pattern_ = nil; + } +} + +Regexp::~Regexp () { + if (pattern_) { + delete [] pattern_; + } + if (c_pattern) { + delete [] c_pattern; + } +} + +const char* Regexp::pattern() const { return pattern_; } + +int Regexp::Search (const char* text, int length, int index, int range) { + bool forwardSearch; + bool frontAnchored; + bool endAnchored; + char* searchStart; + char* searchLimit; + char* endOfLine = nil; + char* lastMatch = nil; + char csave; + + /* + * A small sanity check. Otherwise length is unused in this function. + * This is really what the logic embedded in the old version of this + * routine enforced. + */ + if (index + range > length) { + range = length - index; + if (range < 0) + return -1; + } + + if (c_pattern == nil) { + return -1; + } + + c_pattern->startp[0] = nil; + + if (range < 0) { + forwardSearch = false; + searchLimit = (char *) text + index; + searchStart = (char *) searchLimit + range; /* range is negative */ + } else { + forwardSearch = true; + searchStart = (char *) text + index; + searchLimit = (char *) searchStart + range; + } + + /* Mark end of text string so search will stop */ + char save = *searchLimit; + *searchLimit = '\0'; + + frontAnchored = pattern_[0] == '^'; + endAnchored = pattern_[strlen(pattern_)-1] == '$'; + if (frontAnchored && (searchStart != text && searchStart[-1] != '\n')) { + searchStart = NextLine(searchStart); + } + + while (searchStart && searchStart < searchLimit) { + int result; + + if (endAnchored && (endOfLine = FindNewline(searchStart)) != nil) { + csave = *endOfLine; + *endOfLine = '\0'; + } + + result = regexec(c_pattern, searchStart); + + if (endOfLine) + *endOfLine = csave; + + if (result) { + /* Found a match */ + if (forwardSearch) + break; /* Done */ + else { + lastMatch = c_pattern->startp[0]; + searchStart = c_pattern->endp[0]; + if (frontAnchored) + searchStart = NextLine(searchStart); + continue; + } + } + /* Did not find a match */ + if (frontAnchored || endAnchored) + searchStart = NextLine(searchStart); + else + break; + } + + if (!forwardSearch && lastMatch) { + if (endAnchored && (endOfLine = FindNewline(lastMatch)) != nil) { + csave = *endOfLine; + *endOfLine = '\0'; + } + (void) regexec(c_pattern, lastMatch); /* Refill startp and endp */ + if (endOfLine) + *endOfLine = csave; + } + + *searchLimit = save; + c_pattern->textStart = (char *) text; + + return c_pattern->startp[0] - c_pattern->textStart; +} + +int Regexp::Match (const char* text, int length, int index) { + + if (c_pattern == nil) + return -1; + + c_pattern->startp[0] = nil; + + char save = *(text+length); + *(char*)(text+length) = '\0'; + + c_pattern->textStart = (char *) text; + (void) regexec(c_pattern, (char *) text + index); + + *(char*)(text+length) = save; + + if (c_pattern->startp[0] != nil) + return c_pattern->endp[0] - c_pattern->startp[0]; + else + return -1; +} + +int Regexp::BeginningOfMatch (int subexp) { + if (subexp < 0 || subexp > NSUBEXP || + c_pattern == nil || c_pattern->startp[0] == nil) + return -1; + return c_pattern->startp[subexp] - c_pattern->textStart; +} + +int Regexp::EndOfMatch (int subexp) { + if (subexp < 0 || subexp > NSUBEXP || + c_pattern == nil || c_pattern->startp[0] == nil) + return -1; + return c_pattern->endp[subexp] - c_pattern->textStart; +} + +/* + * regcomp and regexec + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + */ + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or nil + * regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in regexec() needs it and regcomp() is computing + * it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH implement concatenation; a "next" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +/* definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this string. */ +#define BRANCH 6 /* node Match this alternative, or the next... */ +#define BACK 7 /* no Match "", "next" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define STAR 10 /* node Match this (simple) thing 0 or more times. */ +#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ +#define OPEN 20 /* no Mark this point in input as start of #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE 30 /* no Analogous to OPEN. */ + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(p) (*(p)) +#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) +#define OPERAND(p) ((p) + 3) + +/* + * Utility definitions. + */ + +/** + * This replaces a macro of the same name with some bit manipulation magic in + * it. The does not seem well-suited now, but it's not clear that it was before + * either. + */ +inline int UCHARAT(const char* p) { + return *p; +} + +#define FAIL(m) { regerror(m); return(nil); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') +#define META "^$.[()|?+*\\" + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ +#define SPSTART 04 /* Starts with * or +. */ +#define WORST 0 /* Worst case. */ + +/* + * Global work variables for regcomp(). + */ +static const char *regparse; /* Input-scan pointer. */ +static int regnpar; /* () count. */ +static char regdummy; +static char *regcode; /* Code-emit pointer; ®dummy = don't. */ +static long regsize; /* Code size. */ + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +static regexp * +regcomp(const char* exp) { + regexp *r; + char *scan; + char *longest; + int len; + int flags; + + if (exp == nil) + FAIL("nil argument"); + + /* First pass: determine size, legality. */ + regparse = exp; + regnpar = 1; + regsize = 0L; + regcode = ®dummy; + regc(REGEXP_MAGIC); + if (reg(0, &flags) == nil) + return(nil); + + /* Small enough for pointer-storage convention? */ + if (regsize >= 32767L) /* Probably could be 65535L. */ + FAIL("regexp too big"); + + /* Allocate space. */ + r = (regexp *) new char[sizeof(regexp) + (unsigned)regsize]; + + /* Second pass: emit code. */ + regparse = exp; + regnpar = 1; + regcode = r->program; + regc(REGEXP_MAGIC); + if (reg(0, &flags) == nil) { + delete [] r; + return(nil); + } + + /* Dig out information for optimizations. */ + r->regstart = '\0'; /* Worst-case defaults. */ + r->reganch = 0; + r->regmust = nil; + r->regmlen = 0; + scan = r->program+1; /* First BRANCH. */ + if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(scan) == EXACTLY) + r->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + r->reganch++; + + /* + * If there's something expensive in the r.e., find the + * longest literal string that must appear and make it the + * regmust. Resolve ties in favor of later strings, since + * the regstart check works with the beginning of the r.e. + * and avoiding duplication strengthens checking. Not a + * strong reason, but sufficient in the absence of others. + */ + if (flags&SPSTART) { + longest = nil; + len = 0; + for (; scan != nil; scan = regnext(scan)) + if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = strlen(OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } + + return(r); +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static char * +reg(int paren, int* flagp) { + char *ret; + char *br; + char *ender; + int parno; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + if (regnpar >= NSUBEXP) + FAIL("too many ()"); + parno = regnpar; + regnpar++; + ret = regnode(OPEN+parno); + } else + ret = nil; + + /* Pick up the branches, linking them together. */ + br = regbranch(&flags); + if (br == nil) + return(nil); + if (ret != nil) + regtail(ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + while (*regparse == '|') { + regparse++; + br = regbranch(&flags); + if (br == nil) + return(nil); + regtail(ret, br); /* BRANCH -> BRANCH. */ + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode((paren) ? CLOSE+parno : END); + regtail(ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != nil; br = regnext(br)) + regoptail(br, ender); + + /* Check for proper termination. */ + if (paren && *regparse++ != ')') { + FAIL("unmatched ()"); + } else if (!paren && *regparse != '\0') { + if (*regparse == ')') { + FAIL("unmatched ()"); + } else + FAIL("junk on end"); /* "Can't happen". */ + /* NOTREACHED */ + } + + return(ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +static char * +regbranch(int* flagp) { + char *ret; + char *chain; + char *latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(BRANCH); + chain = nil; + while (*regparse != '\0' && *regparse != '|') { + if (*regparse == '\\' && regparse[1] == ')') { + regparse++; + break; + } + latest = regpiece(&flags); + if (latest == nil) + return(nil); + *flagp |= flags&HASWIDTH; + if (chain == nil) /* First piece. */ + *flagp |= flags&SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == nil) /* Loop ran zero times. */ + (void) regnode(NOTHING); + + return(ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +static char * +regpiece(int* flagp) { + char *ret; + char op; + char *next; + int flags; + + ret = regatom(&flags); + if (ret == nil) + return(nil); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return(ret); + } + + if (!(flags&HASWIDTH) && op != '?') + FAIL("*+ operand could be empty"); + *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); + + if (op == '*' && (flags&SIMPLE)) + reginsert(STAR, ret); + else if (op == '*') { + /* Emit x* as (x&|), where & means "self". */ + reginsert(BRANCH, ret); /* Either x */ + regoptail(ret, regnode(BACK)); /* and loop */ + regoptail(ret, ret); /* back */ + regtail(ret, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '+' && (flags&SIMPLE)) + reginsert(PLUS, ret); + else if (op == '+') { + /* Emit x+ as x(&|), where & means "self". */ + next = regnode(BRANCH); /* Either */ + regtail(ret, next); + regtail(regnode(BACK), ret); /* loop back */ + regtail(next, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '?') { + /* Emit x? as (x|) */ + reginsert(BRANCH, ret); /* Either x */ + regtail(ret, regnode(BRANCH)); /* or */ + next = regnode(NOTHING); /* null. */ + regtail(ret, next); + regoptail(ret, next); + } + regparse++; + if (ISMULT(*regparse)) + FAIL("nested *?+"); + + return(ret); +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +static char * +regatom(int* flagp) { + char *ret; + int flags; + + *flagp = WORST; /* Tentatively. */ + + switch (*regparse++) { + case '^': + ret = regnode(BOL); + break; + case '$': + ret = regnode(EOL); + break; + case '.': + ret = regnode(ANY); + *flagp |= HASWIDTH|SIMPLE; + break; + case '[': { + int classbeg; + int classend; + + if (*regparse == '^') { /* Complement of range. */ + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == ']' || *regparse == '-') + regc(*regparse++); + while (*regparse != '\0' && *regparse != ']') { + if (*regparse == '-') { + regparse++; + if (*regparse == ']' || *regparse == '\0') + regc('-'); + else { + classbeg = UCHARAT(regparse-2)+1; + classend = UCHARAT(regparse); + if (classbeg > classend+1) + FAIL("invalid [] range"); + for (; classbeg <= classend; classbeg++) + regc(classbeg); + regparse++; + } + } else + regc(*regparse++); + } + regc('\0'); + if (*regparse != ']') + FAIL("unmatched []"); + regparse++; + *flagp |= HASWIDTH|SIMPLE; + } + break; + case '\0': + case '|': + FAIL("internal urp"); /* Supposed to be caught earlier. */ + break; + case '?': + case '+': + case '*': + FAIL("?+* follows nothing"); + break; + case '\\': + if (*regparse == '\0') + FAIL("trailing \\"); + if (*regparse == '(') { + regparse++; + ret = reg(1, &flags); + if (ret == nil) + return(nil); + *flagp |= flags&(HASWIDTH|SPSTART); + } else { + ret = regnode(EXACTLY); + regc(*regparse++); + regc('\0'); + *flagp |= HASWIDTH|SIMPLE; + } + break; + default: { + int len; + char ender; + + regparse--; + len = strcspn(regparse, META); + if (len <= 0) + FAIL("internal disaster"); + ender = *(regparse+len); + if (len > 1 && ISMULT(ender)) + len--; /* Back off clear of ?+* operand. */ + *flagp |= HASWIDTH; + if (len == 1) + *flagp |= SIMPLE; + ret = regnode(EXACTLY); + while (len > 0) { + regc(*regparse++); + len--; + } + regc('\0'); + } + break; + } + + return(ret); +} + +/* + - regnode - emit a node + */ +static char * /* Location. */ +regnode(char op) { + char *ret; + char *ptr; + + ret = regcode; + if (ret == ®dummy) { + regsize += 3; + return(ret); + } + + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; /* Null "next" pointer. */ + *ptr++ = '\0'; + regcode = ptr; + + return(ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +static void +regc(char b) { + if (regcode != ®dummy) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +static void +reginsert(char op, char* opnd) { + char *src; + char *dst; + char *place; + + if (regcode == ®dummy) { + regsize += 3; + return; + } + + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; /* Op node, where operand used to be. */ + *place++ = op; + *place++ = '\0'; + *place++ = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +static void +regtail(char* p, char* val) { + char *scan; + char *temp; + int offset; + + if (p == ®dummy) + return; + + /* Find last node. */ + scan = p; + for (;;) { + temp = regnext(scan); + if (temp == nil) + break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = scan - val; + else + offset = val - scan; + *(scan+1) = (offset>>8)&0377; + *(scan+2) = offset&0377; +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ +static void +regoptail(char* p, char* val) { + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p == nil || p == ®dummy || OP(p) != BRANCH) + return; + regtail(OPERAND(p), val); +} + +/* + * regexec and friends + */ + +/* + * Global work variables for regexec(). + */ +static char *reginput; /* String-input pointer. */ +static char *regbol; /* Beginning of input, for ^ check. */ +static char **regstartp; /* Pointer to startp array. */ +static char **regendp; /* Ditto for endp. */ + +/* + - regexec - match a regexp against a string + */ +static int +regexec(regexp* prog, char* string) { + char *s; + + /* Be paranoid... */ + if (prog == nil || string == nil) { + regerror("nil parameter"); + return(0); + } + + /* Check validity of program. */ + if (UCHARAT(prog->program) != REGEXP_MAGIC) { + regerror("corrupted program"); + return(0); + } + + /* If there is a "must appear" string, look for it. */ + if (prog->regmust != nil) { + s = string; + while ((s = strchr(s, prog->regmust[0])) != nil) { + if (strncmp(s, prog->regmust, prog->regmlen) == 0) + break; /* Found it. */ + s++; + } + if (s == nil) /* Not present. */ + return(0); + } + + /* Mark beginning of line for ^ . */ + regbol = string; + + /* Simplest case: anchored match need be tried only once. */ + if (prog->reganch) + return(regtry(prog, string)); + + /* Messy cases: unanchored match. */ + s = string; + if (prog->regstart != '\0') + /* We know what char it must start with. */ + while ((s = strchr(s, prog->regstart)) != nil) { + if (regtry(prog, s)) + return(1); + s++; + } + else + /* We don't -- general case. */ + do { + if (regtry(prog, s)) + return(1); + } while (*s++ != '\0'); + + /* Failure. */ + return(0); +} + +/* + - regtry - try match at specific point + */ +static int /* 0 failure, 1 success */ +regtry(regexp* prog, char* string) { + int i; + char **sp; + char **ep; + + reginput = string; + regstartp = prog->startp; + regendp = prog->endp; + + sp = prog->startp; + ep = prog->endp; + for (i = NSUBEXP; i > 0; i--) { + *sp++ = nil; + *ep++ = nil; + } + if (regmatch(prog->program + 1)) { + prog->startp[0] = string; + prog->endp[0] = reginput; + return(1); + } else + return(0); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ +static int /* 0 failure, 1 success */ +regmatch(char* prog) { + char *scan; /* Current node. */ + char *next; /* Next node. */ + + scan = prog; + while (scan != nil) { + next = regnext(scan); + + switch (OP(scan)) { + case BOL: + if (reginput != regbol) + return(0); + break; + case EOL: + if (*reginput != '\0') + return(0); + break; + case ANY: + if (*reginput == '\0') + return(0); + reginput++; + break; + case EXACTLY: { + int len; + char *opnd; + + opnd = OPERAND(scan); + /* Inline the first character, for speed. */ + if (*opnd != *reginput) + return(0); + len = strlen(opnd); + if (len > 1 && strncmp(opnd, reginput, len) != 0) + return(0); + reginput += len; + } + break; + case ANYOF: + if (*reginput == '\0') + return(0); + if (strchr(OPERAND(scan), *reginput) == nil) + return(0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0') + return(0); + if (strchr(OPERAND(scan), *reginput) != nil) + return(0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: { + int no; + char *save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set startp if some later + * invocation of the same parentheses + * already has. + */ + if (regstartp[no] == nil) + regstartp[no] = save; + return(1); + } else + return(0); + } + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: { + int no; + char *save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set endp if some later + * invocation of the same parentheses + * already has. + */ + if (regendp[no] == nil) + regendp[no] = save; + return(1); + } else + return(0); + } + break; + case BRANCH: { + char *save; + + if (OP(next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return(1); + reginput = save; + scan = regnext(scan); + } while (scan != nil && OP(scan) == BRANCH); + return(0); + /* NOTREACHED */ + } + } + break; + case STAR: + case PLUS: { + char nextch; + int no; + char *save; + int min; + + /* + * Lookahead to avoid useless match attempts + * when we know what character comes next. + */ + nextch = '\0'; + if (OP(next) == EXACTLY) + nextch = *OPERAND(next); + min = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= min) { + /* If it could work, try it. */ + if (nextch == '\0' || *reginput == nextch) + if (regmatch(next)) + return(1); + /* Couldn't or didn't -- back up. */ + no--; + reginput = save + no; + } + return(0); + } + break; + case END: + return(1); /* Success! */ + default: + regerror("memory corruption"); + return(0); + } + + scan = next; + } + + /* + * We get here only if there's trouble -- normally "case END" is + * the terminating point. + */ + regerror("corrupted pointers"); + return(0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +static int +regrepeat(char* p) { + int count = 0; + char *scan; + char *opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = strlen(scan); + scan += count; + break; + case EXACTLY: + while (*opnd == *scan) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && strchr(opnd, *scan) != nil) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && strchr(opnd, *scan) == nil) { + count++; + scan++; + } + break; + default: /* Oh dear. Called inappropriately. */ + regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } + reginput = scan; + + return(count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +static char * +regnext(char* p) { + int offset; + + if (p == ®dummy) + return(nil); + + offset = NEXT(p); + if (offset == 0) + return(nil); + + if (OP(p) == BACK) + return(p-offset); + else + return(p+offset); +} + +static void +regerror(const char* s) { + std::cerr << "regexp: " << s << "\n"; +} + diff --git a/src/nrncvode/netcvode.cpp b/src/nrncvode/netcvode.cpp index f86afcfdce..d2dd5ffcb5 100644 --- a/src/nrncvode/netcvode.cpp +++ b/src/nrncvode/netcvode.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include "classreg.h" #include "nrnoc2iv.h" #include "parse.hpp" @@ -930,74 +930,98 @@ Object** NetCvode::netconlist() { Object** po = newoclist(4, o); Object *opre = nullptr, *opost = nullptr, *otar = nullptr; - std::regex spre, spost, star; + Regexp *spre = nullptr, *spost = nullptr, *star = nullptr; + char* s; + int n; if (hoc_is_object_arg(1)) { opre = *hoc_objgetarg(1); } else { - std::string s(gargstr(1)); - if (s.empty()) { - spre = std::regex(".*"); + s = gargstr(1); + if (s[0] == '\0') { + spre = new Regexp(".*"); } else { - try { - spre = std::regex(escape_bracket(s.data())); - } catch (std::regex_error&) { - hoc_execerror(gargstr(1), "not a valid regular expression"); - } + spre = new Regexp(escape_bracket(s)); + } + if (!spre->pattern()) { + delete std::exchange(spre, nullptr); + hoc_execerror(gargstr(1), "not a valid regular expression"); } } if (hoc_is_object_arg(2)) { opost = *hoc_objgetarg(2); } else { - std::string s(gargstr(2)); - if (s.empty()) { - spost = std::regex(".*"); + s = gargstr(2); + if (s[0] == '\0') { + spost = new Regexp(".*"); } else { - try { - spost = std::regex(escape_bracket(s.data())); - } catch (std::regex_error&) { - hoc_execerror(gargstr(2), "not a valid regular expression"); - } + spost = new Regexp(escape_bracket(s)); + } + if (!spost->pattern()) { + delete std::exchange(spost, nullptr); + delete std::exchange(spre, nullptr); + hoc_execerror(gargstr(2), "not a valid regular expression"); } } if (hoc_is_object_arg(3)) { otar = *hoc_objgetarg(3); } else { - std::string s(gargstr(3)); - if (s.empty()) { - star = std::regex(".*"); + s = gargstr(3); + if (s[0] == '\0') { + star = new Regexp(".*"); } else { - try { - star = std::regex(escape_bracket(s.data())); - } catch (std::regex_error&) { - hoc_execerror(gargstr(3), "not a valid regular expression"); - } + star = new Regexp(escape_bracket(s)); + } + if (!star->pattern()) { + delete std::exchange(star, nullptr); + delete std::exchange(spre, nullptr); + delete std::exchange(spost, nullptr); + hoc_execerror(gargstr(3), "not a valid regular expression"); } } + bool b; hoc_Item* q; if (psl_) { ITERATE(q, psl_) { PreSyn* ps = (PreSyn*) VOIDITM(q); - bool b = false; + b = false; if (ps->ssrc_) { Object* precell = nrn_sec2cell(ps->ssrc_); if (opre) { - b = precell == opre; + if (precell == opre) { + b = true; + } else { + b = false; + } } else { - std::string s(hoc_object_name(precell)); - b = std::regex_search(s, spre); + s = hoc_object_name(precell); + n = strlen(s); + if (spre->Match(s, n, 0) > 0) { + b = true; + } else { + b = false; + } } } else if (ps->osrc_) { Object* presyn = ps->osrc_; if (opre) { - b = presyn == opre; + if (presyn == opre) { + b = true; + } else { + b = false; + } } else { - std::string s(hoc_object_name(presyn)); - b = std::regex_search(s, spre); + s = hoc_object_name(presyn); + n = strlen(s); + if (spre->Match(s, n, 0) > 0) { + b = true; + } else { + b = false; + } } } - if (b) { + if (b == true) { for (const auto& d: ps->dil_) { Object* postcell = nullptr; Object* target = nullptr; @@ -1009,19 +1033,37 @@ Object** NetCvode::netconlist() { } } if (opost) { - b = postcell == opost; + if (postcell == opost) { + b = true; + } else { + b = false; + } } else { - std::string s(hoc_object_name(postcell)); - b = std::regex_search(s, spost); + s = hoc_object_name(postcell); + n = strlen(s); + if (spost->Match(s, n, 0) > 0) { + b = true; + } else { + b = false; + } } - if (b) { + if (b == true) { if (otar) { - b = target == otar; + if (target == otar) { + b = true; + } else { + b = false; + } } else { - std::string s(hoc_object_name(target)); - b = std::regex_search(s, star); + s = hoc_object_name(target); + n = strlen(s); + if (star->Match(s, n, 0) > 0) { + b = true; + } else { + b = false; + } } - if (b) { + if (b == true) { o->append(d->obj_); } } @@ -1029,6 +1071,9 @@ Object** NetCvode::netconlist() { } } } + delete std::exchange(spre, nullptr); + delete std::exchange(spost, nullptr); + delete std::exchange(star, nullptr); return po; }