wip initial

This commit is contained in:
Hamed Masafi 2020-08-12 17:02:06 +04:30
parent c382e4d53f
commit de4dc0f6c5
19 changed files with 214 additions and 138 deletions

View File

@ -616,10 +616,10 @@ QString AbstractSqlGenerator::selectCommand(const QString &tableName,
QString sql = QStringLiteral("SELECT ") + selectText QString sql = QStringLiteral("SELECT ") + selectText
+ QStringLiteral(" FROM ") + fromText; + QStringLiteral(" FROM ") + fromText;
if (whereText != QStringLiteral("")) if (!whereText.isEmpty())
sql.append(QStringLiteral(" WHERE ") + whereText); sql.append(QStringLiteral(" WHERE ") + whereText);
if (orderText != QStringLiteral("")) if (!orderText.isEmpty())
sql.append(QStringLiteral(" ORDER BY ") + orderText); sql.append(QStringLiteral(" ORDER BY ") + orderText);
// for (int i = 0; i < _database->model().count(); i++) // for (int i = 0; i < _database->model().count(); i++)
@ -648,7 +648,7 @@ QString AbstractSqlGenerator::selectCommand(const QString &tableName,
QString sql = QStringLiteral("SELECT ") + selectText QString sql = QStringLiteral("SELECT ") + selectText
+ QStringLiteral(" FROM ") + fromText; + QStringLiteral(" FROM ") + fromText;
if (whereText != QStringLiteral("")) if (!whereText.isEmpty())
sql.append(QStringLiteral(" WHERE ") + whereText); sql.append(QStringLiteral(" WHERE ") + whereText);
for (int i = 0; i < _database->model().count(); i++) for (int i = 0; i < _database->model().count(); i++)

View File

@ -46,9 +46,11 @@ HEADERS += \
$$PWD/phrases/phrasedata.h \ $$PWD/phrases/phrasedata.h \
$$PWD/phrases/phrasedatalist.h \ $$PWD/phrases/phrasedatalist.h \
$$PWD/phrases/phraselist.h \ $$PWD/phrases/phraselist.h \
$$PWD/phrases/datephrase.h \ $$PWD/phrases/fieldphrase_date.h \
$$PWD/table_p.h \ $$PWD/table_p.h \
bulkinserter_p.h $$PWD/bulkinserter_p.h \
$$PWD/phrases/fieldphrase_bool.h \
$$PWD/phrases/fieldphrase_qstring.h
SOURCES += \ SOURCES += \
$$PWD/generators/abstractsqlgenerator.cpp \ $$PWD/generators/abstractsqlgenerator.cpp \
@ -77,7 +79,9 @@ SOURCES += \
$$PWD/phrases/phrasedata.cpp \ $$PWD/phrases/phrasedata.cpp \
$$PWD/phrases/phrasedatalist.cpp \ $$PWD/phrases/phrasedatalist.cpp \
$$PWD/phrases/phraselist.cpp \ $$PWD/phrases/phraselist.cpp \
$$PWD/phrases/datephrase.cpp $$PWD/phrases/fieldphrase_date.cpp \
$$PWD/phrases/fieldphrase_bool.cpp \
$$PWD/phrases/fieldphrase_qstring.cpp
load(qt_module) load(qt_module)

View File

@ -30,7 +30,9 @@
#include <QtNut/phrasedata.h> #include <QtNut/phrasedata.h>
#include <QtNut/assignmentphrase.h> #include <QtNut/assignmentphrase.h>
#include <QtNut/numericphrase.h> #include <QtNut/numericphrase.h>
#include <QtNut/datephrase.h> #include <QtNut/fieldphrase_date.h>
#include <QtNut/fieldphrase_qstring.h>
#include <QtNut/fieldphrase_bool.h>
NUT_BEGIN_NAMESPACE NUT_BEGIN_NAMESPACE

View File

@ -19,6 +19,7 @@
**************************************************************************/ **************************************************************************/
#include "abstractfieldphrase.h" #include "abstractfieldphrase.h"
#include <QDebug>
NUT_BEGIN_NAMESPACE NUT_BEGIN_NAMESPACE
@ -34,22 +35,26 @@ AbstractFieldPhrase::AbstractFieldPhrase(const char *className,
AbstractFieldPhrase::AbstractFieldPhrase(const AbstractFieldPhrase &other) AbstractFieldPhrase::AbstractFieldPhrase(const AbstractFieldPhrase &other)
{ {
data = other.data; data = other.data;
data->parents++; data->ref.ref();
} }
AbstractFieldPhrase::AbstractFieldPhrase(AbstractFieldPhrase &&other) AbstractFieldPhrase::AbstractFieldPhrase(AbstractFieldPhrase &&other)
{ {
data = other.data; data = other.data;
data->parents++;
other.data = nullptr; other.data = nullptr;
} }
AbstractFieldPhrase::~AbstractFieldPhrase() AbstractFieldPhrase::~AbstractFieldPhrase()
{ {
if (data) { if (data) {
--data->parents; if (!data->ref.deref()) {
if (data->parents <= 0) qDebug() << "deleted" << data->className
<< data->fieldName;
delete data; delete data;
}
else
qDebug() << "more parents for" << data->className
<< data->fieldName;
} }
} }
@ -87,6 +92,14 @@ AssignmentPhrase AbstractFieldPhrase::operator <<(const QVariant &other)
return AssignmentPhrase(this, other); return AssignmentPhrase(this, other);
} }
void AbstractFieldPhrase::detach()
{
auto clone = data->clone();
if (!data->ref.deref())
delete data;
data = clone;
}
#define AbstractFieldPhraseOperatorVariant(class, op, cond) \ #define AbstractFieldPhraseOperatorVariant(class, op, cond) \
ConditionalPhrase class::operator op(const QVariant &other) \ ConditionalPhrase class::operator op(const QVariant &other) \
{ \ { \

View File

@ -80,6 +80,9 @@ public:
AssignmentPhrase operator =(const QVariant &other); AssignmentPhrase operator =(const QVariant &other);
AssignmentPhrase operator =(const ConditionalPhrase &other); AssignmentPhrase operator =(const ConditionalPhrase &other);
AssignmentPhrase operator <<(const QVariant &other); AssignmentPhrase operator <<(const QVariant &other);
protected:
void detach();
}; };
NUT_END_NAMESPACE NUT_END_NAMESPACE

View File

@ -26,7 +26,7 @@ NUT_BEGIN_NAMESPACE
AssignmentPhrase::AssignmentPhrase(PhraseData *d) : data(d) AssignmentPhrase::AssignmentPhrase(PhraseData *d) : data(d)
{ {
d->parents++; d->ref.ref();
} }
AssignmentPhrase::AssignmentPhrase(AbstractFieldPhrase *l, const QVariant r) AssignmentPhrase::AssignmentPhrase(AbstractFieldPhrase *l, const QVariant r)
@ -56,9 +56,8 @@ AssignmentPhrase::AssignmentPhrase(AssignmentPhrase *ph, const QVariant &v)
AssignmentPhrase::~AssignmentPhrase() AssignmentPhrase::~AssignmentPhrase()
{ {
if (data) if (data && data->ref.deref())
if (!--data->parents) delete data;
delete data;
} }
NUT_END_NAMESPACE NUT_END_NAMESPACE

View File

@ -68,9 +68,9 @@ AssignmentPhraseList AssignmentPhraseList::operator &(const AssignmentPhrase
AssignmentPhraseList::~AssignmentPhraseList() AssignmentPhraseList::~AssignmentPhraseList()
{ {
foreach (PhraseData *d, data) // foreach (PhraseData *d, data)
if (!--d->parents) // if (!d->ref.deref())
delete d; // delete d;
// qDeleteAll(data); // qDeleteAll(data);
// data.clear(); // data.clear();
} }
@ -78,7 +78,7 @@ AssignmentPhraseList::~AssignmentPhraseList()
void AssignmentPhraseList::incAllDataParents() void AssignmentPhraseList::incAllDataParents()
{ {
foreach (PhraseData *d, data) foreach (PhraseData *d, data)
d->parents++; d->ref.ref();
} }

View File

@ -18,6 +18,8 @@
** **
**************************************************************************/ **************************************************************************/
#include <QDebug>
#include "abstractfieldphrase.h" #include "abstractfieldphrase.h"
#include "conditionalphrase.h" #include "conditionalphrase.h"
#include "phrasedata.h" #include "phrasedata.h"
@ -30,21 +32,21 @@ ConditionalPhrase::ConditionalPhrase() : data(nullptr)
ConditionalPhrase::ConditionalPhrase(const ConditionalPhrase &other) ConditionalPhrase::ConditionalPhrase(const ConditionalPhrase &other)
{ {
data = other.data; data = other.data;
data->parents++; data->ref.ref();
// const_cast<ConditionalPhrase&>(other).data = 0;
} }
#ifdef Q_COMPILER_RVALUE_REFS #ifdef Q_COMPILER_RVALUE_REFS
ConditionalPhrase::ConditionalPhrase(ConditionalPhrase &&other) ConditionalPhrase::ConditionalPhrase(ConditionalPhrase &&other)
{ {
this->data = qMove(other.data); data = other.data;
other.data = nullptr;
} }
#endif #endif
ConditionalPhrase::ConditionalPhrase(const PhraseData *data) ConditionalPhrase::ConditionalPhrase(const PhraseData *data)
{ {
this->data = const_cast<PhraseData*>(data); data = const_cast<PhraseData*>(data);
this->data->parents++; data->ref.ref();
} }
ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l, ConditionalPhrase::ConditionalPhrase(AbstractFieldPhrase *l,
@ -104,8 +106,10 @@ ConditionalPhrase::~ConditionalPhrase()
{ {
if (data) { if (data) {
data->cleanUp(); data->cleanUp();
if (!--data->parents) if (!data->ref.deref()) {
qDebug() << "deleted for cond";
delete data; delete data;
}
} }
} }
@ -113,7 +117,7 @@ ConditionalPhrase &ConditionalPhrase::operator =(const ConditionalPhrase &other)
{ {
data = other.data; data = other.data;
if (data) if (data)
data->parents++; data->ref.ref();
return *this; return *this;
} }
@ -122,23 +126,6 @@ ConditionalPhrase ConditionalPhrase::operator ==(const QVariant &other)
return ConditionalPhrase(this, PhraseData::Equal, other); return ConditionalPhrase(this, PhraseData::Equal, other);
} }
//ConditionalPhrase ConditionalPhrase::operator ==(const AbstractFieldPhrase &other)
//{
// return ConditionalPhrase(this, PhraseData::Equal, other);
//}
//ConditionalPhrase ConditionalPhrase::operator &&(const ConditionalPhrase &other)
//{
// return ConditionalPhrase(this, PhraseData::And,
// const_cast<ConditionalPhrase&>(other));
//}
//ConditionalPhrase ConditionalPhrase::operator ||(const ConditionalPhrase &other)
//{
// return ConditionalPhrase(this, PhraseData::Or,
// const_cast<ConditionalPhrase&>(other));
//}
#define DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(op, cond) \ #define DECLARE_CONDITIONALPHRASE_OPERATORS_IMPL(op, cond) \
ConditionalPhrase operator op(const ConditionalPhrase &l, \ ConditionalPhrase operator op(const ConditionalPhrase &l, \
const ConditionalPhrase &r) \ const ConditionalPhrase &r) \
@ -149,8 +136,8 @@ ConditionalPhrase operator op(const ConditionalPhrase &l, \
p.data->operatorCond = cond; \ p.data->operatorCond = cond; \
p.data->left = l.data; \ p.data->left = l.data; \
p.data->right = r.data; \ p.data->right = r.data; \
l.data->parents++; \ l.data->ref.ref(); \
r.data->parents++; \ r.data->ref.ref(); \
return p; \ return p; \
} \ } \
ConditionalPhrase operator op(const ConditionalPhrase &l, \ ConditionalPhrase operator op(const ConditionalPhrase &l, \
@ -162,8 +149,8 @@ ConditionalPhrase operator op(const ConditionalPhrase &l, \
p.data->operatorCond = cond; \ p.data->operatorCond = cond; \
p.data->left = l.data; \ p.data->left = l.data; \
p.data->right = r.data; \ p.data->right = r.data; \
l.data->parents++; \ l.data->ref.ref(); \
r.data->parents++; \ r.data = nullptr; \
return p; \ return p; \
} \ } \
ConditionalPhrase operator op(ConditionalPhrase &&l, \ ConditionalPhrase operator op(ConditionalPhrase &&l, \
@ -175,8 +162,8 @@ ConditionalPhrase operator op(ConditionalPhrase &&l, \
p.data->operatorCond = cond; \ p.data->operatorCond = cond; \
p.data->left = l.data; \ p.data->left = l.data; \
p.data->right = r.data; \ p.data->right = r.data; \
l.data->parents++; \ r.data->ref.ref(); \
r.data->parents++; \ l.data = nullptr; \
return p; \ return p; \
} \ } \
ConditionalPhrase operator op(ConditionalPhrase &&l, ConditionalPhrase &&r) \ ConditionalPhrase operator op(ConditionalPhrase &&l, ConditionalPhrase &&r) \
@ -187,8 +174,8 @@ ConditionalPhrase operator op(ConditionalPhrase &&l, ConditionalPhrase &&r) \
p.data->operatorCond = cond; \ p.data->operatorCond = cond; \
p.data->left = l.data; \ p.data->left = l.data; \
p.data->right = r.data; \ p.data->right = r.data; \
l.data->parents++; \ l.data = nullptr; \
r.data->parents++; \ r.data = nullptr; \
return p; \ return p; \
} }

View File

@ -50,29 +50,6 @@ Q_OUTOFLINE_TEMPLATE ConditionalPhrase FieldPhrase<T>::operator ==(const QVarian
return ConditionalPhrase(this, PhraseData::Equal, other); return ConditionalPhrase(this, PhraseData::Equal, other);
} }
template<>
class FieldPhrase<QString> : public AbstractFieldPhrase
{
public:
FieldPhrase(const char *className, const char *s) :
AbstractFieldPhrase(className, s)
{}
ConditionalPhrase like(const QString &term) {
return ConditionalPhrase(this, PhraseData::Like, term);
}
ConditionalPhrase contains(const QString &term) {
return ConditionalPhrase(this, PhraseData::Like,
QVariant(QStringLiteral("%") + term + QStringLiteral("%")));
}
AssignmentPhrase operator =(const QVariant &v) {
return AssignmentPhrase(this, v);
}
};
//Date and time //Date and time
#define CONDITIONAL_VARIANT_METHOD(name, cond) \ #define CONDITIONAL_VARIANT_METHOD(name, cond) \
ConditionalPhrase name(int val) \ ConditionalPhrase name(int val) \
@ -80,31 +57,6 @@ public:
return ConditionalPhrase(this, cond, val); \ return ConditionalPhrase(this, cond, val); \
} }
template<>
class FieldPhrase<bool> : public AbstractFieldPhrase
{
public:
FieldPhrase(const char *className, const char *s) :
AbstractFieldPhrase(className, s)
{}
AssignmentPhrase operator =(const bool &other) {
return AssignmentPhrase(this, other);
}
FieldPhrase<bool> operator !()
{
FieldPhrase<bool> f(data->className, data->fieldName);
// f.data = new PhraseData(data);
f.data->isNot = !data->isNot;
return f;
}
operator ConditionalPhrase()
{
return ConditionalPhrase(this, PhraseData::Equal, !data->isNot);
}
};
NUT_END_NAMESPACE NUT_END_NAMESPACE

View File

@ -0,0 +1,6 @@
#include "fieldphrase_bool.h"
NUT_BEGIN_NAMESPACE
NUT_END_NAMESPACE

View File

@ -0,0 +1,38 @@
#ifndef NUT_FIELDPHRASE_BOOL_H
#define NUT_FIELDPHRASE_BOOL_H
#include <QtNut/defines.h>
#include <QtNut/fieldphrase.h>
#include <QtNut/fieldphrase.h>
NUT_BEGIN_NAMESPACE
template<>
class FieldPhrase<bool> : public AbstractFieldPhrase
{
public:
FieldPhrase(const char *className, const char *s) :
AbstractFieldPhrase(className, s)
{}
AssignmentPhrase operator =(const bool &other) {
return AssignmentPhrase(this, other);
}
FieldPhrase<bool> operator !()
{
FieldPhrase<bool> f(data->className, data->fieldName);
// f.data = new PhraseData(data);
f.data->isNot = !data->isNot;
return f;
}
operator ConditionalPhrase()
{
return ConditionalPhrase(this, PhraseData::Equal, !data->isNot);
}
};
NUT_END_NAMESPACE
#endif // NUT_FIELDPHRASE_BOOL_H

View File

@ -18,7 +18,7 @@
** **
**************************************************************************/ **************************************************************************/
#include "datephrase.h" #include "fieldphrase_date.h"
NUT_BEGIN_NAMESPACE NUT_BEGIN_NAMESPACE

View File

@ -0,0 +1,2 @@
#include "fieldphrase_qstring.h"

View File

@ -0,0 +1,39 @@
#ifndef NUT_FIELDPHRASE_QSTRING_H
#define NUT_FIELDPHRASE_QSTRING_H
#include <QtNut/defines.h>
#include <QtNut/abstractfieldphrase.h>
#include <QtNut/fieldphrase.h>
NUT_BEGIN_NAMESPACE
template<>
class FieldPhrase<QString> : public AbstractFieldPhrase
{
public:
FieldPhrase(const char *className, const char *s)
: AbstractFieldPhrase(className, s)
{}
ConditionalPhrase like(const QString &term)
{
return ConditionalPhrase(this, PhraseData::Like, term);
}
ConditionalPhrase contains(const QString &term)
{
return ConditionalPhrase(this,
PhraseData::Like,
QVariant(QStringLiteral("%") + term
+ QStringLiteral("%")));
}
AssignmentPhrase operator=(const QVariant &v)
{
return AssignmentPhrase(this, v);
}
};
NUT_END_NAMESPACE
#endif // NUT_FIELDPHRASE_QSTRING_H

View File

@ -22,52 +22,57 @@
NUT_BEGIN_NAMESPACE NUT_BEGIN_NAMESPACE
PhraseData::PhraseData() : PhraseData::PhraseData()
className(""), fieldName(""), : className(""), fieldName(""), type(Field), operatorCond(NotAssign),
type(Field), operatorCond(NotAssign), left(nullptr), right(nullptr), operand(QVariant::Invalid), isNot(false),
left(nullptr), right(nullptr), operand(QVariant::Invalid), isNot(false), parents(1) ref(1)
{ } { }
PhraseData::PhraseData(const char *className, const char *fieldName) : PhraseData::PhraseData(const char *className, const char *fieldName)
className(className), fieldName(fieldName), : className(className), fieldName(fieldName), type(Field),
type(Field), operatorCond(NotAssign), operatorCond(NotAssign), left(nullptr), right(nullptr),
left(nullptr), right(nullptr), operand(QVariant::Invalid), isNot(false), parents(1) operand(QVariant::Invalid), isNot(false), ref(1)
{ } { }
PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o) PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o)
: className(nullptr), fieldName(nullptr), : className(nullptr), fieldName(nullptr), type(WithoutOperand),
type(WithoutOperand), operatorCond(o), left(l), right(nullptr), operatorCond(o), left(l), right(nullptr), isNot(false), ref(1)
isNot(false), parents(1)
{ {
l->parents++; l->ref.ref();
} }
PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, PhraseData *r)
PhraseData *r) : className(nullptr), fieldName(nullptr), type(WithOther), operatorCond(o),
: className(nullptr), fieldName(nullptr), left(l), right(r), isNot(false), ref(1)
type(WithOther), operatorCond(o),
left(l), right(r),
isNot(false), parents(1)
{ {
l->parents++; l->ref.ref();
r->parents++; r->ref.ref();
} }
PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, QVariant r) PhraseData::PhraseData(PhraseData *l, PhraseData::Condition o, QVariant r)
: className(nullptr), fieldName(nullptr), : className(nullptr), fieldName(nullptr), type(WithVariant),
type(WithVariant), operatorCond(o), left(l), operatorCond(o), left(l), right(nullptr), operand(r), isNot(false),
right(nullptr), operand(r), isNot(false), parents(1) ref(1)
{ } { }
PhraseData::~PhraseData()
{
// if (left && !left->ref.deref())
// delete left;
// if (right && !right->ref.deref())
// delete right;
}
PhraseData *PhraseData::operator =(PhraseData *other) PhraseData *PhraseData::operator =(PhraseData *other)
{ {
other->parents++; other->ref.ref();
return other; return other;
} }
PhraseData &PhraseData::operator =(PhraseData &other) PhraseData &PhraseData::operator =(PhraseData &other)
{ {
other.parents++; other.ref.ref();
return other; return other;
} }
@ -81,6 +86,26 @@ void PhraseData::cleanUp()
{ {
} }
PhraseData *PhraseData::clone() const
{
auto c = new PhraseData;
c->className = className;
c->fieldName = fieldName;
c->type = type;
c->operatorCond = operatorCond;
c->left = left;
c->right = right;
c->operand = operand;
c->isNot = isNot;
// c->parents = parents;
return c;
}
void PhraseData::cleanUp(PhraseData *d) void PhraseData::cleanUp(PhraseData *d)
{ {
if (d->left) if (d->left)

View File

@ -97,24 +97,25 @@ public:
QVariant operand; QVariant operand;
bool isNot; bool isNot;
quint16 parents; // quint16 parents;
mutable QAtomicInt ref;
PhraseData(); PhraseData();
PhraseData(const char *className, const char *fieldName); PhraseData(const char *className, const char *fieldName);
PhraseData(PhraseData *l, Condition o); PhraseData(PhraseData *l, Condition o);
PhraseData(PhraseData *l, Condition o, PhraseData *r); PhraseData(PhraseData *l, Condition o, PhraseData *r);
PhraseData(PhraseData *l, Condition o, QVariant r); PhraseData(PhraseData *l, Condition o, QVariant r);
// explicit PhraseData(const PhraseData &other);
// explicit PhraseData(const PhraseData *other); virtual ~PhraseData();
PhraseData *operator =(PhraseData *other); PhraseData *operator =(PhraseData *other);
PhraseData &operator =(PhraseData &other); PhraseData &operator =(PhraseData &other);
QString toString() const; QString toString() const;
~PhraseData() = default;
void cleanUp(); void cleanUp();
PhraseData *clone() const;
private: private:
void cleanUp(PhraseData *d); void cleanUp(PhraseData *d);
}; };

View File

@ -37,14 +37,14 @@ PhraseDataList::PhraseDataList(const PhraseDataList &other) : QList<PhraseData*>
void PhraseDataList::append(PhraseData *d) void PhraseDataList::append(PhraseData *d)
{ {
d->parents++; d->ref.ref();
QList<PhraseData*>::append(d); QList<PhraseData*>::append(d);
} }
void PhraseDataList::append(QList<PhraseData *> &dl) void PhraseDataList::append(QList<PhraseData *> &dl)
{ {
foreach (PhraseData *d, dl) foreach (PhraseData *d, dl)
d->parents++; d->ref.ref();
QList<PhraseData*>::append(dl); QList<PhraseData*>::append(dl);
} }
@ -53,7 +53,7 @@ PhraseDataList::~PhraseDataList()
QList<PhraseData*>::iterator i; QList<PhraseData*>::iterator i;
for (i = begin(); i != end(); ++i) { for (i = begin(); i != end(); ++i) {
(*i)->cleanUp(); (*i)->cleanUp();
if (!--(*i)->parents) if (!(*i)->ref.deref())
delete *i; delete *i;
} }
} }

View File

@ -3,6 +3,7 @@
#include "tst_phrases.h" #include "tst_phrases.h"
#include "phrase.h" #include "phrase.h"
#include "sqlitegenerator.h"
using namespace Nut; using namespace Nut;
@ -17,11 +18,15 @@ void PhrasesTest::initTestCase()
void PhrasesTest::no1() void PhrasesTest::no1()
{ {
FieldPhrase<int> id("main", "id"); {
FieldPhrase<QString> name("main", "name"); FieldPhrase<int> id("main", "id");
FieldPhrase<QString> last_name("main", "last_name"); FieldPhrase<QString> name("main", "name");
FieldPhrase<QDate> date("main", "date"); FieldPhrase<QString> last_name("main", "last_name");
auto w = (id == 4 && name == "hi"); FieldPhrase<QDate> date("main", "date");
auto w = (id == 4 && name == QStringLiteral("hi"));
SqliteGenerator g;
}
} }
void PhrasesTest::numeric() void PhrasesTest::numeric()