diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index ed463738..ec0a3b76 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1230,6 +1230,38 @@ jobs: fi echo "[dict-source] -DICTSOURCE marks dictionary nodes with file and line" + - name: iscon, isstr, isnum, isfloat, ispn, isarray (#447) (Non-20.04) + if: matrix.id != 'docker-ubuntu-20.04' + shell: bash + run: | + set -eo pipefail + # Type tests on each kind of value (#447). Columns of 1/0 are + # con str num float pn array. An unset variable reads as 0. + FIX=.github/workflows/tests/type-tests + rm -rf "$FIX/logs" "$FIX/input/text.txt_log" "$FIX/output" + 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(type-tests): no output.txt -- the analyzer did not build"; exit 1 + fi + got=$(tr -d '\r' < "$OUT") + want=$(printf '%s\n' \ + 'concept 100000' \ + 'string 010000' \ + 'digits 010000' \ + 'empty 010000' \ + 'number 001000' \ + 'float 000100' \ + 'node 000010' \ + 'array 000001' \ + 'unset 001000' \ + 'direct 1111') + if [ "$got" != "$want" ]; then + echo "FAIL(type-tests): output.txt was:"; echo "$got"; echo "wanted:"; echo "$want"; exit 1 + fi + echo "[type-tests] iscon, isstr, isnum, isfloat, ispn and isarray" + - 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 70383d3f..9b645bfc 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -1122,6 +1122,37 @@ jobs: fi echo "[dict-source] -DICTSOURCE marks dictionary nodes with file and line" + - name: iscon, isstr, isnum, isfloat, ispn, isarray (#447) + shell: bash + run: | + set -eo pipefail + # Type tests on each kind of value (#447). Columns of 1/0 are + # con str num float pn array. An unset variable reads as 0. + FIX=.github/workflows/tests/type-tests + rm -rf "$FIX/logs" "$FIX/input/text.txt_log" "$FIX/output" + 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(type-tests): no output.txt -- the analyzer did not build"; exit 1 + fi + got=$(tr -d '\r' < "$OUT") + want=$(printf '%s\n' \ + 'concept 100000' \ + 'string 010000' \ + 'digits 010000' \ + 'empty 010000' \ + 'number 001000' \ + 'float 000100' \ + 'node 000010' \ + 'array 000001' \ + 'unset 001000' \ + 'direct 1111') + if [ "$got" != "$want" ]; then + echo "FAIL(type-tests): output.txt was:"; echo "$got"; echo "wanted:"; echo "$want"; exit 1 + fi + echo "[type-tests] iscon, isstr, isnum, isfloat, ispn and isarray" + - name: factorial() up to 20! shell: bash run: | diff --git a/.github/workflows/tests/type-tests/input/text.txt b/.github/workflows/tests/type-tests/input/text.txt new file mode 100644 index 00000000..85af2da6 --- /dev/null +++ b/.github/workflows/tests/type-tests/input/text.txt @@ -0,0 +1 @@ +Hello 42 world diff --git a/.github/workflows/tests/type-tests/spec/analyzer.seq b/.github/workflows/tests/type-tests/spec/analyzer.seq new file mode 100644 index 00000000..bbe4cd10 --- /dev/null +++ b/.github/workflows/tests/type-tests/spec/analyzer.seq @@ -0,0 +1,2 @@ +tokenize nil # +nlp types # diff --git a/.github/workflows/tests/type-tests/spec/types.nlp b/.github/workflows/tests/type-tests/spec/types.nlp new file mode 100644 index 00000000..4a81a6d2 --- /dev/null +++ b/.github/workflows/tests/type-tests/spec/types.nlp @@ -0,0 +1,28 @@ +############################################### +# FILE: types.nlp +# SUBJ: iscon, isstr, isnum, isfloat, ispn, isarray on each kind of +# value (#447). Columns: con str num float pn array. +# NOTE: Expected output is in the CI step. +############################################### + +@DECL +show(L("label"), L("v")) { + "output.txt" << L("label") << " " << iscon(L("v")) << isstr(L("v")) << isnum(L("v")) + << isfloat(L("v")) << ispn(L("v")) << isarray(L("v")) << "\n"; +} +@@DECL + +@CODE +show("concept", makeconcept(findroot(), "typeTest")); +show("string", "abc"); +show("digits", "42"); +show("empty", ""); +show("number", 42); +show("float", 4.5); +show("node", pndown(pnroot())); +L("arr")[0] = "a"; +L("arr")[1] = "b"; +show("array", L("arr")); +show("unset", L("nothing")); +"output.txt" << "direct " << iscon(findroot()) << isnum(3) << isstr("x") << ispn(pnroot()) << "\n"; +@@CODE diff --git a/lite/fn.cpp b/lite/fn.cpp index 67a4c5d9..56c05d75 100644 --- a/lite/fn.cpp +++ b/lite/fn.cpp @@ -460,6 +460,18 @@ switch (fnid) // 12/21/01 AM. return fnCallanalyzer(args,nlppp,/*UP*/sem); // 09/13/26 DD. case FNcallconcept: // In callana.cpp. return fnCallconcept(args,nlppp,/*UP*/sem); // 09/13/26 DD. + case FNiscon: + return fnIstype(args,nlppp,/*UP*/sem,0); // #447. + case FNisstr: + return fnIstype(args,nlppp,/*UP*/sem,1); // #447. + case FNisnum: + return fnIstype(args,nlppp,/*UP*/sem,2); // #447. + case FNisfloat: + return fnIstype(args,nlppp,/*UP*/sem,3); // #447. + case FNispn: + return fnIstype(args,nlppp,/*UP*/sem,4); // #447. + case FNisarray: + return fnIstype(args,nlppp,/*UP*/sem,5); // #447. case FNlogten: return fnLogten(args,nlppp,/*UP*/sem); // 04/29/04 AM. case FNrandomint: @@ -14940,6 +14952,71 @@ return true; * FORMS: abs(num) ********************************************/ +/******************************************** +* FN: FNISTYPE +* SUBJ: Type tests on any value (#447). +* RET: 1 or 0 in sem. Never warns, even for an empty value. +* FORMS: iscon(x) isstr(x) isnum(x) isfloat(x) ispn(x) isarray(x) +* NOTE: KIND is the name's index in that list. An array is a value with +* more than one element; a numeric string is a string, not a num. +********************************************/ + +bool Fn::fnIstype( + Delt *args, + Nlppp *nlppp, + RFASem* &sem, + int kind + ) +{ +static const _TCHAR *names[] = { + _T("iscon"), _T("isstr"), _T("isnum"), _T("isfloat"), _T("ispn"), _T("isarray") }; +sem = 0; +Parse *parse = nlppp->parse_; +if (args && args->Right()) + { + _stprintf(Errbuf, _T("[%s: Takes one argument.]"), names[kind]); + return parse->errOut(false); + } + +bool yes = false; +Iarg *arg = args ? args->getData() : 0; +RFASem *val = 0; +switch (arg ? arg->getType() : IANULL) + { + case IASTR: + yes = (kind == 1); + break; + case IANUM: + yes = (kind == 2); + break; + case IAFLOAT: + yes = (kind == 3); + break; + case IASEM: + val = arg->getSem(); + if (!val) + break; + switch (val->getType()) + { + case RS_KBCONCEPT: yes = (kind == 0); break; + case RSSTR: + case RSNAME: yes = (kind == 1); break; + case RSLONG: + case RSNUM: yes = (kind == 2); break; + case RSFLOAT: yes = (kind == 3); break; + case RSNODE: yes = (kind == 4); break; + case RSARGS: yes = (kind == 5); break; + default: break; + } + break; + default: + break; + } +sem = new RFASem(yes ? 1LL : 0LL); +return true; +} + + bool Fn::fnAbs( Delt *args, Nlppp *nlppp, diff --git a/lite/fn.h b/lite/fn.h index 1e6e4b08..6d9cf671 100644 --- a/lite/fn.h +++ b/lite/fn.h @@ -986,6 +986,7 @@ static bool fnStruniquechars( static bool fnJsonparse(Delt*,Nlppp*,RFASem*&); // 08/04/26 DD. static bool fnCallanalyzer(Delt*,Nlppp*,RFASem*&); // 09/13/26 DD. static bool fnCallconcept(Delt*,Nlppp*,RFASem*&); // 09/13/26 DD. + static bool fnIstype(Delt*,Nlppp*,RFASem*&,int); // #447. static bool fnStriscaps(Delt*,Nlppp*,RFASem*&); static bool fnStrisupper(Delt*,Nlppp*,RFASem*&); // 11/20/01 AM. static bool fnStrislower(Delt*,Nlppp*,RFASem*&); // 11/20/01 AM. diff --git a/lite/func_defs.h b/lite/func_defs.h index bdf12a80..a1fc9cd1 100644 --- a/lite/func_defs.h +++ b/lite/func_defs.h @@ -406,6 +406,14 @@ enum funcDef FNcallanalyzer, FNcallconcept, + // Type tests. // #447. + FNiscon, + FNisstr, + FNisnum, + FNisfloat, + FNispn, + FNisarray, + FNXXYYZZ }; #endif diff --git a/lite/funcs.h b/lite/funcs.h index cab8c92b..29497ed8 100644 --- a/lite/funcs.h +++ b/lite/funcs.h @@ -394,6 +394,13 @@ _T("jsonparse"), // Analyzer functions. // 09/13/26 DD. _T("callanalyzer"), _T("callconcept"), +// Type tests. // #447. +_T("iscon"), +_T("isstr"), +_T("isnum"), +_T("isfloat"), +_T("ispn"), +_T("isarray"), 0 }; #endif // NLP_LITE_FUNCS_H diff --git a/lite/iaction.cpp b/lite/iaction.cpp index 8c9b8eac..2fc51f89 100644 --- a/lite/iaction.cpp +++ b/lite/iaction.cpp @@ -811,6 +811,12 @@ static const _TCHAR *bridgedFns[] = { _T("dirlist"), _T("fileexists"), _T("filesize"), + _T("isarray"), // #447. + _T("iscon"), + _T("isfloat"), + _T("isnum"), + _T("ispn"), + _T("isstr"), _T("jsonparse"), _T("jsonwrite"), _T("push"), diff --git a/nlp/main.cpp b/nlp/main.cpp index 39411872..2cda5df3 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.33" +#define NLP_ENGINE_VERSION "4.2.34" bool cmdReadArgs(int, _TCHAR *argv[], _TCHAR *&, _TCHAR *&, _TCHAR *&, _TCHAR *&, bool &, bool &, bool &, bool &, bool &, bool &, int &, bool &, bool &); void cmdHelpargs(_TCHAR *);