Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,40 @@ jobs:
fi
echo "[literal] makeliteral() makes a token later passes match"

- name: attr="value" on rule elements (#438) (Non-20.04)
if: matrix.id != 'docker-ubuntu-20.04'
shell: bash
run: |
set -eo pipefail
# A key on a rule element that is not a modifier, with one value, is
# an attribute test (#438): _xALPHA [pos="noun"] is vareq=("pos" "noun").
# Digits compare as a number, as do the values of vareq=/vargt=/...
# written on an element. A key one edit from a modifier warns.
FIX=.github/workflows/tests/element-eq
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(element-eq): no output.txt -- the analyzer did not build"; exit 1
fi
got=$(tr -d '\r' < "$OUT")
want=$(printf '%s\n' \
'text attr=value: two' \
'text vareq=: two' \
'number attr=value: three' \
'number vareq=: three' \
'both attr=value: two' \
'both vareq=: two' \
'greater: three')
if [ "$got" != "$want" ]; then
echo "FAIL(element-eq): output.txt was:"; echo "$got"; echo "wanted:"; echo "$want"; exit 1
fi
if ! grep -q "Element key=mtch is not a modifier.*Did you mean match" "$FIX/logs/make_ana.log"; then
echo "FAIL(element-eq): no typo warning for mtch= in make_ana.log"; cat "$FIX/logs/make_ana.log"; exit 1
fi
echo "[element-eq] attr=value on an element tests the attribute"

- name: factorial() up to 20! (Non-20.04)
if: matrix.id != 'docker-ubuntu-20.04'
shell: bash
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,39 @@ jobs:
fi
echo "[literal] makeliteral() makes a token later passes match"

- name: attr="value" on rule elements (#438)
shell: bash
run: |
set -eo pipefail
# A key on a rule element that is not a modifier, with one value, is
# an attribute test (#438): _xALPHA [pos="noun"] is vareq=("pos" "noun").
# Digits compare as a number, as do the values of vareq=/vargt=/...
# written on an element. A key one edit from a modifier warns.
FIX=.github/workflows/tests/element-eq
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(element-eq): no output.txt -- the analyzer did not build"; exit 1
fi
got=$(tr -d '\r' < "$OUT")
want=$(printf '%s\n' \
'text attr=value: two' \
'text vareq=: two' \
'number attr=value: three' \
'number vareq=: three' \
'both attr=value: two' \
'both vareq=: two' \
'greater: three')
if [ "$got" != "$want" ]; then
echo "FAIL(element-eq): output.txt was:"; echo "$got"; echo "wanted:"; echo "$want"; exit 1
fi
if ! grep -q "Element key=mtch is not a modifier.*Did you mean match" "$FIX/logs/make_ana.log"; then
echo "FAIL(element-eq): no typo warning for mtch= in make_ana.log"; cat "$FIX/logs/make_ana.log"; exit 1
fi
echo "[element-eq] attr=value on an element tests the attribute"

- name: factorial() up to 20!
shell: bash
run: |
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tests/element-eq/input/text.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
one two three
10 changes: 10 additions & 0 deletions .github/workflows/tests/element-eq/spec/analyzer.seq
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tokenize nil #
nlp mark #
nlp text_eq #
nlp text_vareq #
nlp number_eq #
nlp number_vareq #
nlp both_eq #
nlp both_vareq #
nlp greater #
nlp typo #
14 changes: 14 additions & 0 deletions .github/workflows/tests/element-eq/spec/both_eq.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###############################################
# FILE: both_eq.nlp
# SUBJ: An attribute test beside a match list and a repeat. (attr=value form)
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
"output.txt" << "both attr=value: " << phrasetext() << "\n";
noop();
@RULES
_xNIL <- _xWILD [one match=(one two) pos="noun"] @@
14 changes: 14 additions & 0 deletions .github/workflows/tests/element-eq/spec/both_vareq.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###############################################
# FILE: both_vareq.nlp
# SUBJ: An attribute test beside a match list and a repeat. (vareq= form)
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
"output.txt" << "both vareq=: " << phrasetext() << "\n";
noop();
@RULES
_xNIL <- _xWILD [one match=(one two) vareq=("pos" "noun")] @@
14 changes: 14 additions & 0 deletions .github/workflows/tests/element-eq/spec/greater.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###############################################
# FILE: greater.nlp
# SUBJ: vargt= on an element compares with the number 2, not the text "2".
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
"output.txt" << "greater: " << phrasetext() << "\n";
noop();
@RULES
_xNIL <- _xALPHA [vargt=("count" 2)] @@
20 changes: 20 additions & 0 deletions .github/workflows/tests/element-eq/spec/mark.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
###############################################
# FILE: mark.nlp
# SUBJ: Gives "two" pos=noun and "three" count=3.
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
N("pos",1) = "noun";
noop();
@RULES
_xNIL <- two @@

@POST
N("count",1) = 3;
noop();
@RULES
_xNIL <- three @@
14 changes: 14 additions & 0 deletions .github/workflows/tests/element-eq/spec/number_eq.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###############################################
# FILE: number_eq.nlp
# SUBJ: A number attribute; digits compare as a number. (attr=value form)
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
"output.txt" << "number attr=value: " << phrasetext() << "\n";
noop();
@RULES
_xNIL <- _xALPHA [count=3] @@
14 changes: 14 additions & 0 deletions .github/workflows/tests/element-eq/spec/number_vareq.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###############################################
# FILE: number_vareq.nlp
# SUBJ: A number attribute; digits compare as a number. (vareq= form)
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
"output.txt" << "number vareq=: " << phrasetext() << "\n";
noop();
@RULES
_xNIL <- _xALPHA [vareq=("count" 3)] @@
14 changes: 14 additions & 0 deletions .github/workflows/tests/element-eq/spec/text_eq.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###############################################
# FILE: text_eq.nlp
# SUBJ: A text attribute. (attr=value form)
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
"output.txt" << "text attr=value: " << phrasetext() << "\n";
noop();
@RULES
_xNIL <- _xALPHA [pos="noun"] @@
14 changes: 14 additions & 0 deletions .github/workflows/tests/element-eq/spec/text_vareq.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###############################################
# FILE: text_vareq.nlp
# SUBJ: A text attribute. (vareq= form)
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
"output.txt" << "text vareq=: " << phrasetext() << "\n";
noop();
@RULES
_xNIL <- _xALPHA [vareq=("pos" "noun")] @@
14 changes: 14 additions & 0 deletions .github/workflows/tests/element-eq/spec/typo.nlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###############################################
# FILE: typo.nlp
# SUBJ: mtch is one edit from match: warns, then tests an attribute "mtch".
# NOTE: Part of the element-eq regression (#438); expected output
# is in the CI step.
###############################################

@NODES _ROOT

@POST
"output.txt" << "typo: " << phrasetext() << "\n";
noop();
@RULES
_xNIL <- _xALPHA [mtch=(two)] @@
102 changes: 98 additions & 4 deletions lite/ielt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1354,11 +1354,23 @@ for (dpair = list->getFirst(); dpair; dpair = dpair->Right())
Dlist<Iarg> *args = vals ? Iarg::copy_args(vals, parse, true) : 0;
// Element values are strings; the actions that take numbers get
// numbers, as they do from a @PRE region (the compiled form needs it).
if (args && (!strcmp_i(key, _T("numrange"))
|| !strcmp_i(key, _T("length"))
|| !strcmp_i(key, _T("lengthr"))))
// For the var comparisons only the value (second argument) can be a
// number: vargt=("count" 2) must compare with 2, not "2".
Delt<Iarg> *from = 0;
if (!args)
;
else if (!strcmp_i(key, _T("numrange"))
|| !strcmp_i(key, _T("length"))
|| !strcmp_i(key, _T("lengthr")))
from = args->getFirst();
else if (!strcmp_i(key, _T("vareq"))
|| !strcmp_i(key, _T("varne"))
|| !strcmp_i(key, _T("vargt"))
|| !strcmp_i(key, _T("varlt")))
from = args->getFirst() ? args->getFirst()->Right() : 0;
if (from)
{
for (Delt<Iarg> *darg = args->getFirst(); darg; darg = darg->Right())
for (Delt<Iarg> *darg = from; darg; darg = darg->Right())
{
Iarg *arg = darg->getData();
long long num = 0;
Expand All @@ -1374,6 +1386,40 @@ for (dpair = list->getFirst(); dpair; dpair = dpair->Right())
elt->pres_ = new Dlist<Ipre>();
elt->pres_->rpush(new Ipre(name, args, ord, ord));
}
else if (vals && vals->getFirst() && !vals->getFirst()->Right())
{
// attr="value" on the element is an attribute test (#438):
// _xALPHA [pos="noun"] is vareq=("pos" "noun") is <n,n> vareq("pos","noun");
// A value of digits only compares as a number. A misspelled modifier
// would land here too, so warn when the key is one edit from one.
const _TCHAR *similar = nearModifier(key);
if (similar)
{
std::_t_strstream gerrStr;
gerrStr << _T("[Element key=") << key << _T(" is not a modifier; testing it as an attribute. Did you mean ")
<< similar << _T("?]") << std::ends;
parse->errOut(&gerrStr,false,true);
}
_TCHAR *name = 0, *attr = 0;
parse->internStr(_T("vareq"), /*UP*/ name);
parse->internStr(key, /*UP*/ attr);
Dlist<Iarg> *args = new Dlist<Iarg>();
args->rpush(new Iarg(attr));
Iarg *val = vals->getFirst()->getData();
long long num = 0;
_TCHAR *vstr = (val->getType() == IASTR) ? val->getStr() : 0;
bool digits = vstr && *vstr;
for (_TCHAR *p = vstr; digits && *p; ++p)
if (*p < '0' || *p > '9')
digits = false;
if (digits && str_to_long(vstr, num))
args->rpush(new Iarg(num));
else
args->rpush(Iarg::copy_arg(val));
if (!elt->pres_)
elt->pres_ = new Dlist<Ipre>();
elt->pres_->rpush(new Ipre(name, args, ord, ord));
}
else
{
std::_t_strstream gerrStr;
Expand Down Expand Up @@ -1422,6 +1468,54 @@ else if (elt->max_ == -1)
}


/********************************************
* FN: NEARMODIFIER
* SUBJ: The element modifier KEY is one edit away from, if any (#438).
* NOTE: An unknown key with a value is an attribute test, so a typo such as
* mtch=(...) would silently test an attribute named "mtch". Used to warn.
********************************************/

static bool oneEdit(const _TCHAR *a, const _TCHAR *b)
{
size_t la = _tcslen(a), lb = _tcslen(b);
if (la > lb + 1 || lb > la + 1)
return false;
size_t i = 0, j = 0;
int edits = 0;
while (i < la && j < lb)
{
if (_totlower(a[i]) == _totlower(b[j]))
{ ++i; ++j; continue; }
if (++edits > 1)
return false;
if (la > lb) ++i;
else if (lb > la) ++j;
else { ++i; ++j; }
}
edits += (int) ((la - i) + (lb - j));
return edits == 1;
}

const _TCHAR *Ielt::nearModifier(_TCHAR *key)
{
static const _TCHAR *mods[] = {
_T("attr"), _T("attrs"), _T("base"), _T("deaccent"), _T("dejunk"), _T("except"),
_T("excepts"), _T("fail"), _T("fails"), _T("group"), _T("layer"), _T("layers"),
_T("look"), _T("lookahead"), _T("match"), _T("matches"), _T("max"), _T("min"),
_T("nest"), _T("one"), _T("opt"), _T("option"), _T("optional"), _T("pass"),
_T("passes"), _T("plus"), _T("recurse"), _T("rename"), _T("singlet"), _T("star"),
_T("tree"), _T("trig"), _T("trigger"), _T("unsealed"), _T("xsinglet"), _T("xtree"),
0
};
if (!key || _tcslen(key) < 3)
return 0; // Too short to tell a typo from a real attribute name.
for (const _TCHAR **p = mods; *p; ++p)
if (oneEdit(key, *p))
return *p;
return 0;
}


/********************************************
* FN: PREACTIONKEY
* SUBJ: Whether an element key names a @PRE action (#438).
Expand Down
1 change: 1 addition & 0 deletions lite/ielt.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ class Ielt : public Ielement
/*UP*/ bool &info, bool &f_look,
bool fhash = true);
static bool preActionKey(_TCHAR *key); // #438.
static const _TCHAR *nearModifier(_TCHAR *key); // #438.
void internPres( // 07/13/99 AM.
Dlist<Ipre> *pres, // For binding pre actions.
int ord // Current element number in rule's phrase.
Expand Down
2 changes: 1 addition & 1 deletion nlp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ All rights reserved.
#include "lite/nlp_engine.h"
#include "version.h"

#define NLP_ENGINE_VERSION "4.2.28"
#define NLP_ENGINE_VERSION "4.2.29"

bool cmdReadArgs(int, _TCHAR *argv[], _TCHAR *&, _TCHAR *&, _TCHAR *&, _TCHAR *&, bool &, bool &, bool &, bool &, bool &, bool &, int &, bool &);
void cmdHelpargs(_TCHAR *);
Expand Down
Loading