diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index bf2b12e2..9faf9627 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -946,6 +946,37 @@ jobs: fi echo "[array-index] cached array indexing gives the same values" + - name: Postfix ++ and -- on an array element (Non-20.04) + if: matrix.id != 'docker-ubuntu-20.04' + shell: bash + run: | + set -eo pipefail + # Postfix ++ and -- ignored the index of an array element, saw several + # values, and stopped with the multi-valued variable error. + # Prefix ++x[i] worked. Now x[i]++ changes that element and returns + # its old value, in the interpreter and in compiled analyzers. + FIX=.github/workflows/tests/postfix-index + rm -rf "$FIX/logs" "$FIX/input/text.txt_log" + mkdir -p "$FIX/logs" + ./bin/nlp -ANA "$FIX" -WORK ./ "$FIX/input/text.txt" + OUT="$FIX/input/text.txt_log/output.txt" + if [ ! -f "$OUT" ]; then + echo "FAIL(postfix-index): no output.txt -- the analyzer did not build"; exit 1 + fi + got=$(tr -d '\r' < "$OUT") + want=$(printf '%s\n' \ + 'inc returns 20, now 10 21 30' \ + 'dec returns 30, now 10 21 29' \ + 'padded: 0 0 1 length 6' \ + 'string: 42' \ + 'local: 0 2' \ + 'scalar: 7 then 8' \ + 'node: 5 6') + if [ "$got" != "$want" ]; then + echo "FAIL(postfix-index): output.txt was:"; echo "$got"; echo "wanted:"; echo "$want"; exit 1 + fi + echo "[postfix-index] x[i]++ and x[i]-- change that element" + - name: factorial() up to 20! (Non-20.04) if: matrix.id != 'docker-ubuntu-20.04' shell: bash diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 971217a9..ed49a10f 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -846,6 +846,36 @@ jobs: fi echo "[array-index] cached array indexing gives the same values" + - name: Postfix ++ and -- on an array element + shell: bash + run: | + set -eo pipefail + # Postfix ++ and -- ignored the index of an array element, saw several + # values, and stopped with the multi-valued variable error. + # Prefix ++x[i] worked. Now x[i]++ changes that element and returns + # its old value, in the interpreter and in compiled analyzers. + FIX=.github/workflows/tests/postfix-index + rm -rf "$FIX/logs" "$FIX/input/text.txt_log" + mkdir -p "$FIX/logs" + ./bin/Release/nlp.exe -ANA "$FIX" -WORK ./ "$FIX/input/text.txt" + OUT="$FIX/input/text.txt_log/output.txt" + if [ ! -f "$OUT" ]; then + echo "FAIL(postfix-index): no output.txt -- the analyzer did not build"; exit 1 + fi + got=$(tr -d '\r' < "$OUT") + want=$(printf '%s\n' \ + 'inc returns 20, now 10 21 30' \ + 'dec returns 30, now 10 21 29' \ + 'padded: 0 0 1 length 6' \ + 'string: 42' \ + 'local: 0 2' \ + 'scalar: 7 then 8' \ + 'node: 5 6') + if [ "$got" != "$want" ]; then + echo "FAIL(postfix-index): output.txt was:"; echo "$got"; echo "wanted:"; echo "$want"; exit 1 + fi + echo "[postfix-index] x[i]++ and x[i]-- change that element" + - name: factorial() up to 20! shell: bash run: | diff --git a/.github/workflows/tests/postfix-index/input/text.txt b/.github/workflows/tests/postfix-index/input/text.txt new file mode 100644 index 00000000..4f5b2786 --- /dev/null +++ b/.github/workflows/tests/postfix-index/input/text.txt @@ -0,0 +1 @@ +word diff --git a/.github/workflows/tests/postfix-index/spec/analyzer.seq b/.github/workflows/tests/postfix-index/spec/analyzer.seq new file mode 100644 index 00000000..4a864c7d --- /dev/null +++ b/.github/workflows/tests/postfix-index/spec/analyzer.seq @@ -0,0 +1,3 @@ +tokenize nil # +nlp postfix # x[i]++ and x[i]-- on array elements +nlp nodevar # on a node variable diff --git a/.github/workflows/tests/postfix-index/spec/nodevar.nlp b/.github/workflows/tests/postfix-index/spec/nodevar.nlp new file mode 100644 index 00000000..dde66c50 --- /dev/null +++ b/.github/workflows/tests/postfix-index/spec/nodevar.nlp @@ -0,0 +1,15 @@ +############################################### +# FILE: nodevar.nlp +# SUBJ: Postfix ++ on an element of a node variable's array. +############################################### + +@NODES _ROOT + +@POST + N("c")[0] = 5; + N("c")[1] = 5; + N("c")[1]++; + "output.txt" << "node: " << N("c")[0] << " " << N("c")[1] << "\n"; + noop(); +@RULES +_xNIL <- _xALPHA @@ diff --git a/.github/workflows/tests/postfix-index/spec/postfix.nlp b/.github/workflows/tests/postfix-index/spec/postfix.nlp new file mode 100644 index 00000000..98926ad6 --- /dev/null +++ b/.github/workflows/tests/postfix-index/spec/postfix.nlp @@ -0,0 +1,43 @@ +############################################### +# FILE: postfix.nlp +# SUBJ: Postfix ++ and -- on an array element. They ignored the index, +# saw several values, and failed with "Can't increment +# multi-valued variable". Prefix ++x[i] already worked. +# NOTE: Part of the postfix-index regression; expected output is in +# the CI step. +############################################### + +@CODE +G("a")[0] = 10; +G("a")[1] = 20; +G("a")[2] = 30; + +L("r") = G("a")[1]++; +"output.txt" << "inc returns " << L("r") << ", now " << G("a")[0] << " " << G("a")[1] << " " << G("a")[2] << "\n"; + +L("r") = G("a")[2]--; +"output.txt" << "dec returns " << L("r") << ", now " << G("a")[0] << " " << G("a")[1] << " " << G("a")[2] << "\n"; + +# Past the end: pads with zeros, like assigning to x[i]. +G("a")[5]++; +"output.txt" << "padded: " << G("a")[3] << " " << G("a")[4] << " " << G("a")[5] << " length " << arraylength(G("a")) << "\n"; + +# A numeric string element. +G("s")[0] = "x"; +G("s")[1] = "41"; +G("s")[1]++; +"output.txt" << "string: " << G("s")[1] << "\n"; + +# An index from an expression, on a local array. +L("x")[0] = 1; +L("x")[1] = 1; +L("i") = 1; +L("x")[L("i")]++; +L("x")[L("i") - 1]--; +"output.txt" << "local: " << L("x")[0] << " " << L("x")[1] << "\n"; + +# Plain variables are unchanged. +G("n") = 7; +L("r") = G("n")++; +"output.txt" << "scalar: " << L("r") << " then " << G("n") << "\n"; +@@CODE diff --git a/include/Api/lite/Arun.h b/include/Api/lite/Arun.h index 6c95f4fd..e89bdd38 100644 --- a/include/Api/lite/Arun.h +++ b/include/Api/lite/Arun.h @@ -24,6 +24,7 @@ class Seqn; class Nlppp; class RFASem; class Iarg; // 03/11/02 AM. +class Ipair; // x[i]++ helpers. class CG; // 12/02/02 AM. // May want to determine this for each analyzer or pass. @@ -771,6 +772,10 @@ class LITE_API Arun static RFASem *minus(RFASem *); // Postunary. + static Iarg *index_arg(Ipair *pair, long long index); // x[i], padded. + static bool bump_arg(Iarg *arg, long long delta, long long &old); + static long long postinc(int,_TCHAR*,int,RFASem*,Nlppp*); // x[expr]++ + static long long postdec(int,_TCHAR*,int,RFASem*,Nlppp*); // x[expr]-- static long long postinc(RFASem *,Nlppp *); static long long postinc( // 06/02/00 AM. int typ, diff --git a/lite/Arun.cpp b/lite/Arun.cpp index 628aa3b4..106c7fc9 100644 --- a/lite/Arun.cpp +++ b/lite/Arun.cpp @@ -13161,6 +13161,63 @@ return !flg; } +/******************************************** +* FN: INDEX_ARG +* SUBJ: Element INDEX of a variable's values, padding with zeros up to it, +* as assigning to x[i] does. For postfix x[i]++ and x[i]--. +********************************************/ + +Iarg *Arun::index_arg(Ipair *pair, long long index) +{ +if (!pair || index < 0) + return 0; +Dlist *vals = pair->getVals(); +if (!vals) + { + vals = new Dlist(); + pair->setVals(vals); + } +long reached = 0; +Delt *darg = vals->getNthCached((long) index, reached); +if (!darg) + { + for (long long ii = reached; ii <= index; ++ii) + vals->rpush(new Iarg(0LL)); + darg = vals->getLast(); + } +return darg->getData(); +} + + +/******************************************** +* FN: BUMP_ARG +* SUBJ: Add DELTA to a numeric (or numeric-string) value in place. +* RET: True if done; OLD gets the value before the change. +********************************************/ + +bool Arun::bump_arg(Iarg *arg, long long delta, long long &old) +{ +old = 0; +if (!arg) + return false; +switch (arg->getType()) + { + case IANUM: + old = arg->getNum(); + arg->setNum(old + delta); + return true; + case IASTR: + if (!str_to_long(arg->getStr(), old)) + return false; + arg->setType(IANUM); + arg->setNum(old + delta); + return true; + default: + return false; + } +} + + /******************************************** * FN: POSTINC * CR: 06/02/00 AM. @@ -13181,6 +13238,16 @@ Ipair *pair; Ivar::getVar((enum Ivartype)typ,varname,nelt,nlppp,/*UP*/pair); if (!pair) return ret; // 06/05/00 AM. +if (index >= 0) // x[i]++ : the indexed element. + { + if (!bump_arg(index_arg(pair, index), 1, ret)) + { + std::_t_strstream gerrStr; + gerrStr << _T("[inc: Error. Bad type.]") << std::ends; + nlppp->parse_->errOut(&gerrStr,false); + } + return ret; + } Dlist *vals = pair->getVals(); Delt *darg; @@ -13240,6 +13307,16 @@ Ipair *pair; Ivar::getVar((enum Ivartype)typ,varname,nelt,nlppp,/*UP*/pair); if (!pair) return ret; // 06/05/00 AM. +if (index >= 0) // x[i]-- : the indexed element. + { + if (!bump_arg(index_arg(pair, index), -1, ret)) + { + std::_t_strstream gerrStr; + gerrStr << _T("[dec: Error. Bad type.]") << std::ends; + nlppp->parse_->errOut(&gerrStr,false); + } + return ret; + } Dlist *vals = pair->getVals(); Delt *darg; @@ -13283,6 +13360,33 @@ return ret; /******************************************************************************/ + + +// x[expr]++ / x[expr]-- where the index is an expression. +long long Arun::postinc(int typ, _TCHAR *varname, int nelt, + RFASem *index_sem, Nlppp *nlppp) +{ +bool ok = false; +long long index = index_sem ? index_sem->sem_to_long(ok) : FULLARRAY; +if (index_sem) + delete index_sem; +if (!ok) + index = FULLARRAY; +return postinc(typ, varname, nelt, index, nlppp); +} + +long long Arun::postdec(int typ, _TCHAR *varname, int nelt, + RFASem *index_sem, Nlppp *nlppp) +{ +bool ok = false; +long long index = index_sem ? index_sem->sem_to_long(ok) : FULLARRAY; +if (index_sem) + delete index_sem; +if (!ok) + index = FULLARRAY; +return postdec(typ, varname, nelt, index, nlppp); +} + /******************************************** * FN: SORTVALS * CR: 06/04/00 AM. diff --git a/lite/iexpr.cpp b/lite/iexpr.cpp index 727a06d3..c2b7f09b 100644 --- a/lite/iexpr.cpp +++ b/lite/iexpr.cpp @@ -3242,6 +3242,21 @@ switch (type_) ok = false; goto done; } + if (index >= 0) // x[i]++ : the indexed element. + { + if (!Arun::bump_arg(Arun::index_arg(pair, index), 1, num)) + { + ruleError(line_,nlppp); + nlppp->parse_->line_ = line_; + std::_t_strstream gerrStr; + gerrStr << _T("[inc: Error. Bad type.]") << std::ends; + nlppp->parse_->errOut(&gerrStr,false); + ok = false; + goto done; + } + val = new RFASem(num); // The value before the change. + goto done; + } Delt *darg; Iarg *arg; if (!vals || !(darg = vals->getFirst())) @@ -3303,6 +3318,21 @@ switch (type_) ok = false; goto done; } + if (index >= 0) // x[i]-- : the indexed element. + { + if (!Arun::bump_arg(Arun::index_arg(pair, index), -1, num)) + { + ruleError(line_,nlppp); + nlppp->parse_->line_ = line_; + std::_t_strstream gerrStr; + gerrStr << _T("[dec: Error. Bad type.]") << std::ends; + nlppp->parse_->errOut(&gerrStr,false); + ok = false; + goto done; + } + val = new RFASem(num); // The value before the change. + goto done; + } if (!vals || !(darg = vals->getFirst())) { // Variable has no value. Set to -1 and done. diff --git a/nlp/main.cpp b/nlp/main.cpp index 1d13174e..8717f9a4 100644 --- a/nlp/main.cpp +++ b/nlp/main.cpp @@ -15,7 +15,7 @@ All rights reserved. #include "lite/nlp_engine.h" #include "version.h" -#define NLP_ENGINE_VERSION "4.2.23" +#define NLP_ENGINE_VERSION "4.2.24" bool cmdReadArgs(int, _TCHAR *argv[], _TCHAR *&, _TCHAR *&, _TCHAR *&, _TCHAR *&, bool &, bool &, bool &, bool &, bool &, bool &, int &, bool &); void cmdHelpargs(_TCHAR *);