Files
secondo/include/NList.h
2026-01-23 17:03:45 +08:00

868 lines
18 KiB
C++

/*
----
This file is part of SECONDO.
Copyright (C) 2004-2007, University in Hagen,
Faculty of Mathematics and Computer Science,
Database Systems for New Applications.
SECONDO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
SECONDO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
August 2005, M. Spiekermann. Initial version.
January - March 2006, M. Spiekermann. New constructors and functions added.
Documentation revised.
April 2006, M. Spiekermann. Helpful functions for type mappings added.
*/
#ifndef SECONDO_NLIST_H
#define SECONDO_NLIST_H
#include <assert.h>
#include <sstream>
#include <set>
#include "SecondoException.h"
#include "NestedList.h"
#include "LogMsg.h"
/*
1 Overview
The class ~NList~ can be used as replacement for the syntactical ~noisy~
interface defined in ~NestedList.h~. Currently, not all methods of the ~old~
interface are supported, but may be easily added when needed.
This interface simplifes the check of a list structure, e.g. member function
~isSymbol()~ will return TRUE, if ~n~ is a symbol atom. I think this will make
type mappings better readable since most Operations are done by
~list.function()~ instead of ~nl->Function(list)~. For example retrieving the
second element of the first element can be done by
---- old: nl->Second(nl->First(list))
new: nlist.first().second()
----
Conversion from/to type ~ListExpr~ which is used inside SECONDO whenever nested
lists are used is done by
---- #include "NList.h"
ListExpr f(ListExpr list)
{
Nlist nlist(list);
...
return nlist.listExpr();
}
----
*/
/*
2 String Constants
Type ~Symbols~ defines some string constants for symbols reserved as keywords in
the query processor or often used types. This should be used to avoid writing
always redundant string constants into the code.
*/
struct Symbols {
Symbols() {}
static const std::string& STRING() {
static std::string s("string"); return s;
}
#ifdef SECONDO_WIN32
#undef TEXT
#endif
static const std::string& TEXT() {
static std::string s("text"); return s;
}
static const std::string& INT() {
static std::string s("int"); return s;
}
static const std::string& REAL() {
static std::string s("real"); return s;
}
static const std::string& real() {
static std::string s("real"); return s;
}
static const std::string& BOOL() {
static std::string s("bool"); return s;
}
static const std::string& REL() {
static std::string s("rel"); return s;
}
static const std::string& TUPLE() {
static std::string s("tuple"); return s;
}
static const std::string& PTUPLE() {
static std::string s("ptuple"); return s;
}
static const std::string& STREAM() {
static std::string s("stream"); return s;
}
static const std::string& MAP() {
static std::string s("map"); return s;
}
static const std::string& TYPEERROR() {
static std::string s("typeerror"); return s;
}
static const std::string& APPEND() {
static std::string s("APPEND"); return s;
}
};
class NList;
std::ostream& operator<<(std::ostream& os, const NList& n);
// defined in Secondointerface.cpp
extern Symbols sym;
/*
3 Exception Class ~NListErr~
A class representing nested list exceptions.
*/
class NListErr : public SecondoException {
public:
NListErr(const std::string& Msg) : SecondoException(Msg) {}
NListErr(const Cardinal n, const Cardinal len, const std::string& ext="") :
SecondoException()
{
std::stringstream s;
s << "Out of range exception in nested list! "
<< "Element " << n << " requested, but "
<< "list has length " << len << "." << ext;
msgStr = s.str();
}
const std::string msg() { return msgStr; }
};
#define CHECK(n) {if (n>0 && length()<n) { \
throw NListErr("Element "#n" not in list!"); } }
/*
4 Class ~NList~
This is a ~wrapper~ class for values of type ~ListExpr~.
*/
class NList {
public:
/*
2.2 Construction and Destruction
* ~NList()~: Empty List.
* NList(const NList\& rhs): Create a new reference to ~rhs~.
* ~NList(const ListExpr list)~: Initialize with ~list~.
* ~NList(const NList\& a, const NList\& b)~: Create a two elem list ~(a b)~.
* ~NList(const NList\& a, const NList\& b, const NList\& c)~:
Three elems ~(a b c)~.
* ~NList(const string\& s, const bool isStr = false)~: Create a symbol
or string atom.
*/
NList(NestedList* ptr = nlGlobal) :
nl(ptr),
l(nl->Empty()),
e(nl->Empty()),
len(0)
{}
NList(const NList& rhs) :
nl(rhs.nl),
l(rhs.l),
e(rhs.e),
len(rhs.len)
{}
NList(const ListExpr list, NestedList* ptr = nlGlobal) :
nl(ptr),
l(list),
e(nl->Empty())
{ findEnd();
}
NList(const ListExpr list, int knownLen, NestedList* ptr = nlGlobal) :
nl(ptr),
l(list),
e(nl->Empty()),
len(knownLen)
{
findEnd();
}
NList(const NList& a, const NList& b) :
nl(nlGlobal),
l( nl->TwoElemList(a.l, b.l) ),
e( b.l ),
len(2)
{ findEnd(); }
NList(const NList& a, const NList& b, const NList& c) :
nl(nlGlobal),
l( nl->ThreeElemList(a.l, b.l, c.l) ),
e( c.l ),
len(3)
{ findEnd(); }
NList(const NList& a, const NList& b, const NList& c, const NList& d) :
nl(nlGlobal),
l( nl->FourElemList(a.l, b.l, c.l, d.l) ),
e( d.l ),
len(4)
{ findEnd(); }
NList(const NList& a, const NList& b, const NList& c, const NList& d,
const NList& _e) :
nl(nlGlobal),
l( nl->FiveElemList(a.l, b.l, c.l, d.l, _e.l) ),
e( _e.l ),
len(5)
{ findEnd(); }
NList(const NList& a, const NList& b, const NList& c, const NList& d,
const NList& _e,const NList& f) :
nl(nlGlobal),
l( nl->SixElemList(a.l, b.l, c.l, d.l, _e.l, f.l) ),
e( f.l ),
len(6)
{ findEnd();}
// INSTANTIATION PATTERNS:
// (value), (value, false), (value, false, false) : symbolAtom = DEFAULT1
// (value, true), (value, true, false) : stringAtom = DEFAULT2
// (value, true, true ) : textAtom
NList(const std::string& s, const bool isStr = false,
const bool isText = false):
nl(nlGlobal),
e( nl->Empty() ),
len(0)
{
if (isStr && !isText)
l = nl->StringAtom(s);
else if (isStr && isText)
l = nl->TextAtom(s);
else
l = nl->SymbolAtom(s);
}
NList(const std::string& s1, const std::string& s2) :
nl(nlGlobal),
len(2)
{
e = nl->SymbolAtom(s2);
l = nl->TwoElemList(nl->SymbolAtom(s1), e);
}
NList(const std::string& s1, const std::string& s2, const std::string& s3) :
nl(nlGlobal),
len(3)
{
e = nl->SymbolAtom(s3);
l = nl->ThreeElemList(nl->SymbolAtom(s1), nl->SymbolAtom(s2), e);
}
NList(const bool b, const bool dummy __attribute__((unused))) :
nl(nlGlobal),
l( nl->BoolAtom(b) ),
e( nl->Empty() ),
len(0)
{}
NList(const int n) :
nl(nlGlobal),
l( nl->IntAtom(n) ),
e( nl->Empty() ),
len(0)
{}
NList(const double d) :
nl(nlGlobal),
l( nl->RealAtom(d) ),
e( nl->Empty() ),
len(0)
{}
inline NList& boolAtom(const bool val)
{
nl = nlGlobal;
len = 0;
e = nl->Empty();
l = nl->BoolAtom(val);
return *this;
}
inline NList& intAtom(const int val)
{
nl = nlGlobal;
len = 0;
e = nl->Empty();
l = nl->IntAtom(val);
return *this;
}
inline NList& realAtom(const double val)
{
nl = nlGlobal;
len = 0;
e = nl->Empty();
l = nl->RealAtom(val);
return *this;
}
inline NList& stringAtom(const std::string& val)
{
nl = nlGlobal;
len = 0;
e = nl->Empty();
l = nl->StringAtom(val);
return *this;
}
inline NList& textAtom(const std::string& val)
{
nl = nlGlobal;
len = 0;
e = nl->Empty();
l = nl->TextAtom(val);
return *this;
}
inline NList& symbolAtom(const std::string& val)
{
nl = nlGlobal;
len = 0;
e = nl->Empty();
l = nl->SymbolAtom(val);
return *this;
}
inline NList& enclose()
{
nl = nlGlobal;
len = 1;
l = nl->OneElemList(l);
e = l;
return *this;
}
~NList() {}
/*
2.3 Structure Tests and Value Retrieval
The following functions can be used to get information about the
list stucture and to extract subexpressions or atom values.
*/
inline bool isEmpty() const { return nl->IsEmpty(l); }
inline bool isEqual(const std::string& s) const {
return nl->IsEqual(l, s);
}
inline bool isAtom() const { return nl->IsAtom(l); }
inline bool isSymbol(const Cardinal n = 0) const {
CHECK(n) return isNodeType(n, SymbolType);
}
inline bool isSymbol(const std::string& s) const {
return isNodeType(0, SymbolType) && (s == nl->SymbolValue(l));
}
inline bool isString(const Cardinal n = 0) const {
CHECK(n) return isNodeType(n, StringType);
}
inline bool isText(const Cardinal n = 0) const {
CHECK(n) return isNodeType(n, TextType);
}
inline bool isNoAtom(const Cardinal n = 0) const {
CHECK(n) return isNodeType(n, NoAtom);
}
inline bool isList(const Cardinal n = 0) const {
CHECK(n) return isNodeType(n, NoAtom);
}
inline bool isInt(const Cardinal n = 0) const {
CHECK(n) return isNodeType(n, IntType);
}
inline bool isReal(const Cardinal n = 0) const {
CHECK(n) return isNodeType(n, RealType);
}
inline bool isBool(const Cardinal n = 0) const {
CHECK(n) return isNodeType(n, BoolType);
}
inline bool hasLength(const Cardinal c) const { return (length() == c); }
inline NList first() const { return elem(1); }
inline NList second() const { return elem(2); }
inline NList third() const { return elem(3); }
inline NList fourth() const { return elem(4); }
inline NList fifth() const { return elem(5); }
inline NList sixth() const { return elem(6); }
inline NList seventh() const { return elem(7); }
inline NList eigth() const { return elem(8); }
inline NList nineth() const { return elem(9); }
inline NList tenth() const { return elem(10); }
inline NList eleventh() const {return elem(11); }
inline NList twelfth() const {return elem(12); }
inline Cardinal length() const
{
return len;
}
inline NList elem(Cardinal n) const
{
if ( (length() < n) ) {
throw NListErr(n,len);
}
if ( (n >= 1) && isAtom() ) {
throw NListErr("Element 1 requested but list is an Atom!");
}
return NList(nl->Nth(n,l));
}
// for usage in type mappings
inline static ListExpr typeError(const std::string& msg)
{
ErrorReporter::ReportError(msg);
return nlGlobal->TypeError();
}
inline static ListExpr typeError( int argNumber,
const std::string& expectedListStruct )
{
std::stringstream msg;
msg << "Expecting list structure " << expectedListStruct
<< " for argument number "<< argNumber << ".";
ErrorReporter::ReportError( msg.str() );
return nlGlobal->TypeError();
}
// interchange with type ~ListExpr~
inline ListExpr listExpr() const { return l; }
/*
Functions applicable for ~symbol/string/text~ atoms.
*/
inline bool hasStringValue() const
{
return (isString() || isText() || isSymbol());
}
inline std::string str() const // retrieve a string value
{
static std::string result = "";
if( !hasStringValue() )
throw NListErr("List has no string value exception!");
if ( isSymbol() )
return nl->SymbolValue(l);
if ( isString() )
return nl->StringValue(l);
if ( isText() )
return nl->Text2String(l);
return result;
}
inline int intval() const
{
return nl->IntValue(l);
}
inline double realval() const
{
return nl->RealValue(l);
}
inline int boolval() const
{
return nl->BoolValue(l);
}
/*
Conversion to C++ strings or a string Atom. The latter conversion can be
used to convert a symbol or text atom into a string atom.
*/
inline std::string convertToString() const { return nl->ToString(l); }
inline NList toStringAtom() {
const std::string& s = str();
return NList(s, true);
}
/*
Comparison between ~NList/NList~ and ~NList/string~ instances
*/
inline bool operator==(const NList& rhs) const
{
return nl->Equal(l,rhs.l);
}
inline bool operator!=(const NList& rhs) const
{
return !(*this == rhs);
}
inline bool operator==(const std::string& rhs) const
{
if( !hasStringValue() )
{
return false;
}
else
{
return ( str() == rhs );
}
}
inline bool operator<(const NList& rhs) const
{
return (len < rhs.len);
}
/*
Write the text or binary representation of the nested list
to a given ~ostream~ reference.
*/
inline bool writeAsStringTo( std::ostream& os ) const
{
return nl->WriteStringTo(l,os);
}
inline bool writeAsBinaryTo( std::ostream& os ) const
{
return nl->WriteBinaryTo(l,os);
}
/*
Construction of bigger lists
*/
inline void makeHead(const NList& head)
{
l = nl->OneElemList( head.l );
e = l;
len = 1;
}
inline void append(const NList& tail)
{
if(this->isEmpty()){
*this = tail;
makeHead(*this);
return;
}
if (len == 0) { // an atom
makeHead((*this));
}
e = nl->Append(e, tail.l);
len++;
}
inline void concat(const NList& tail){
if(this->isEmpty()){
(*this) = tail;
return;
}
if(tail.isEmpty()){
return;
}
if(tail.isAtom()){
append(tail);
return;
}
if(isAtom()){
makeHead(*this);
}
NList rest(tail);
while(!rest.isEmpty()){
append(rest.first());
rest.rest();
}
}
/*
Iteration over lists
The function ~rest~ removes the first element of a list.
*/
inline void rest() {
l = nl->Rest(l);
if(nl->IsEmpty(l)){
e = nl->TheEmptyList();
}
}
/*
3 Support for implementing type mappings
The functions below may be useful for implementing type mapping
functions.
*/
inline bool checkAtomList(NList al)
{
while( !al.isEmpty() )
{
if ( ! al.first().isAtom() ) {
return false;
}
al.rest();
}
return true;
}
inline bool checkSymbolList(NList sl)
{
while( !sl.isEmpty() )
{
if ( ! sl.first().isSymbol() ) {
return false;
}
sl.rest();
}
return true;
}
inline bool checkUniqueMembers(NList m)
{
std::set<std::string> tree;
while( !m.isEmpty() )
{
if ( tree.find(m.first().convertToString()) != tree.end() ) {
return false;
}
tree.insert( m.first().convertToString() );
m.rest();
}
return true;
}
inline bool checkStream(NList& type)
{
return ( hasLength(2) && first().isSymbol(sym.STREAM())
&& second() == type );
}
inline NList& streamOf(NList& type)
{
l = nl->OneElemList( nl->SymbolAtom(sym.STREAM()) );
e = l;
len = 1;
append(type);
return *this;
}
inline NList& tupleOf(NList& type)
{
l = nl->OneElemList( nl->SymbolAtom(sym.TUPLE()) );
e = l;
len = 1;
append(type);
return *this;
}
inline NList& tupleStreamOf(NList& type)
{
l = nl->OneElemList( nl->SymbolAtom(sym.STREAM()) );
e = l;
len = 1;
append( NList().tupleOf(type) );
return *this;
}
inline bool checkStreamTuple(NList& attrs)
{
return checkDepth3(sym.STREAM(), sym.TUPLE(), attrs);
}
inline bool checkRel(NList& attrs)
{
return checkDepth3(sym.REL(), sym.TUPLE(), attrs);
}
inline bool checkLength(const int len, std::string& err)
{
if ( !hasLength(len) )
{
std::stringstream s;
s << "List length unequal " << len << ", " << err;
err = s.str();
return false;
}
return true;
}
/*
The function below resets the global nested list pointer. This
is necessary to switch from Kernel-Instance to the Application-Instance.
Use with care! Normally you will not need this.
*/
static void setNLRef(NestedList* ptr) { nlGlobal = ptr; }
static NestedList* getNLRef(){
return nlGlobal;
}
void showNLRefs() {
std::cerr << "nlGlobal:" << nlGlobal << std::endl;
std::cerr << "nl:" << nl << std::endl;
}
std::ostream& print(std::ostream& o){
o << "[NList : l =" << nl->ToString(l) << ", e = " << nl->ToString(e)
<< ", len = " << len << "]";
return o;
}
private:
static NestedList* nlGlobal;
NestedList* nl;
ListExpr l;
ListExpr e; // points to the last element of a list
Cardinal len;
inline bool isNodeType(const int n, const NodeType t) const
{
if ( !n )
return ( /*nl->IsAtom(l) &&*/ (nl->AtomType(l) == t) );
ListExpr list = nl->Nth(n,l);
return ( /*nl->IsAtom(list) &&*/ (nl->AtomType(list) == t) );
}
inline bool checkDepth3(const std::string& s1,
const std::string& s2, NList& attrs)
{
if ( !hasLength(2) )
return false;
if ( !first().isSymbol(s1) )
return false;
NList s = second();
if ( !s.hasLength(2) )
return false;
if ( !s.first().isSymbol(s2) )
return false;
if ( !s.second().isList() )
return false;
attrs = s.second();
return true;
}
/*
~findEnd~ sets the internal pointer to the end of the list to the correct value.
If the list is just an atom, the end will be an empty list.
*/
void findEnd(){
if(nl->IsAtom(l)){
e = nl->TheEmptyList();
len = 0;
} else if(nl->IsEmpty(l)){
e = l;
len = 0;
} else {
e = l;
len = 1;
while(!nl->IsEmpty(nl->Rest(e))){
e = nl->Rest(e);
len++;
}
}
}
};
#endif