1043 lines
27 KiB
C++
1043 lines
27 KiB
C++
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2004, University in Hagen, Department of 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
|
|
----
|
|
|
|
1 Implementation Module Function Algebra
|
|
|
|
January 26, 2001 RHG
|
|
|
|
April 2002 Ulrich Telle Port to C++
|
|
|
|
December 2005, Victor Almeida deleted the deprecated algebra levels
|
|
(~executable~, ~descriptive~, and ~hibrid~). Only the executable
|
|
level remains. Models are also removed from type constructors.
|
|
|
|
The sole purpose of this little algebra is to provide a type constructor ~map~
|
|
which can be used to store the list expressions defining functions
|
|
(abstractions).
|
|
|
|
March 2006 M. Spiekermann, operators ~within~ and ~within2~ modified.
|
|
|
|
*/
|
|
|
|
#include "Algebra.h"
|
|
#include "NestedList.h"
|
|
#include "NList.h"
|
|
#include "QueryProcessor.h"
|
|
#include "AlgebraManager.h"
|
|
#include "StandardTypes.h"
|
|
#include "ListUtils.h"
|
|
#include "Symbols.h"
|
|
#include <string>
|
|
|
|
using namespace std;
|
|
|
|
|
|
extern NestedList* nl;
|
|
extern QueryProcessor *qp;
|
|
extern AlgebraManager *am;
|
|
|
|
namespace FunctionAlgebra{
|
|
|
|
/*
|
|
2.1 Dummy Functions
|
|
|
|
The next function defines the type property of type constructor ~map~.
|
|
|
|
*/
|
|
ListExpr
|
|
FunctionProperty()
|
|
{
|
|
ListExpr remarkslist = nl->TextAtom();
|
|
nl->AppendText(remarkslist,"Stores list expressions defining functions "
|
|
"(internal use).");
|
|
|
|
return (nl->TwoElemList(
|
|
nl->OneElemList(nl->StringAtom("Remarks")),
|
|
nl->OneElemList(remarkslist)));
|
|
}
|
|
|
|
Word
|
|
NoSpace( const ListExpr typeInfo )
|
|
{
|
|
return (SetWord( Address( 0 ) ));
|
|
}
|
|
|
|
void
|
|
DoNothing( const ListExpr typeInfo, Word& w )
|
|
{
|
|
w.addr = 0;
|
|
}
|
|
|
|
Word
|
|
CloneNothing( const ListExpr typeInfo, const Word& w )
|
|
{
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
int
|
|
SizeOfNothing()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
2.2 Type Constructor ~map~
|
|
|
|
*/
|
|
Word
|
|
InMap( const ListExpr typeInfo, ListExpr instance,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct )
|
|
{
|
|
/*
|
|
We don't do any checks here; any list expression will be accepted.
|
|
Errors will be found when the function is used, i.e., sent to the
|
|
query processor.
|
|
|
|
*/
|
|
correct = true;
|
|
nl->IncReferences(instance);
|
|
return (SetWord( instance ));
|
|
}
|
|
|
|
ListExpr
|
|
OutMap( ListExpr typeInfo, Word value )
|
|
{
|
|
nl->IncReferences(value.list);
|
|
return (value.list);
|
|
}
|
|
|
|
void*
|
|
DummyCast( void* addr )
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
NullSize()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
CheckMap( ListExpr type, ListExpr& errorInfo )
|
|
{
|
|
// (map arg_1 arg_2 ... arg_n res) , n may be 0
|
|
return ((nl->ListLength(type) >1) &&
|
|
nl->IsEqual( nl->First( type ), Symbol::MAP() ));
|
|
}
|
|
|
|
TypeConstructor functionMap( Symbol::MAP(), FunctionProperty,
|
|
OutMap, InMap,
|
|
0, 0,
|
|
NoSpace, DoNothing,
|
|
0, 0,
|
|
DoNothing, CloneNothing,
|
|
DummyCast, NullSize, CheckMap );
|
|
|
|
/*
|
|
2.3 Type Operators ~ANY~ and ~ANY2~
|
|
|
|
Type operators are used only for inferring argument types of parameter
|
|
functions. They have a type mapping but no evaluation function.
|
|
|
|
2.3.1 Type mapping function of operator ~ANY~
|
|
|
|
The type operator ~ANY~ corresponds to the type of the first argument.
|
|
|
|
---- (t1 t2 ... tn) -> t1
|
|
----
|
|
|
|
The type operator ~ANY2~ corresponds to the type of the second argument.
|
|
|
|
---- (t1 t2 ... tn) -> t2
|
|
----
|
|
|
|
*/
|
|
ListExpr ANYTypeMap( ListExpr args )
|
|
{
|
|
NList type(args);
|
|
if(type.length() >= 1)
|
|
{
|
|
return type.first().listExpr();
|
|
}
|
|
return NList::typeError(
|
|
"Type Map Operator 'ANY' expects a parameter list of length >=1");
|
|
}
|
|
|
|
const string ANYSpec =
|
|
"(( \"Signature\" \"Syntax\" \"Meaning\" \"Remarks\" )"
|
|
"( <text>(t1 t2 ... tn) -> t1</text--->"
|
|
"<text>type operator</text--->"
|
|
"<text>Simply returns the type of the first argument.</text--->"
|
|
"<text></text---> ))";
|
|
|
|
Operator ANY (
|
|
"ANY",
|
|
ANYSpec,
|
|
0,
|
|
Operator::SimpleSelect,
|
|
ANYTypeMap );
|
|
|
|
|
|
ListExpr ANY2TypeMap( ListExpr args )
|
|
{
|
|
NList type(args);
|
|
if(type.length() >= 2)
|
|
{
|
|
return type.second().listExpr();
|
|
}
|
|
return NList::typeError(
|
|
"Type Map Operator 'ANY2' expects a parameter list of length >=2");
|
|
}
|
|
|
|
const string ANY2Spec =
|
|
"(( \"Signature\" \"Syntax\" \"Meaning\" \"Remarks\" )"
|
|
"( <text>(t1 t2 ... tn) -> t1 -> t2</text--->"
|
|
"<text>type operator</text--->"
|
|
"<text>Simply returns the type of the second argument.</text--->"
|
|
"<text></text---> ))";
|
|
|
|
Operator ANY2 (
|
|
"ANY2",
|
|
ANY2Spec,
|
|
0,
|
|
Operator::SimpleSelect,
|
|
ANY2TypeMap );
|
|
|
|
|
|
|
|
/*
|
|
2.4 Operator ~within~
|
|
|
|
2.4.1 Type mapping function of operator ~within~
|
|
|
|
Result type of within operation.
|
|
|
|
---- ( a (map a b) ) -> b
|
|
---- ( a b (map a b c) ) -> c
|
|
|
|
*/
|
|
ListExpr WithinTypeMap(ListExpr Args)
|
|
{
|
|
NList args(Args);
|
|
NList mapResult;
|
|
|
|
static const string typeA = "(obj_a (map type_a type_b))";
|
|
static const string typeB = "(obj_a obj_b (map type_a type_b type_c))";
|
|
|
|
bool ok = false;
|
|
|
|
try {
|
|
switch (args.length())
|
|
{
|
|
case 2: {
|
|
|
|
if(listutils::isStream(args.first().listExpr())){
|
|
return listutils::typeError("first argument cannot be a stream");
|
|
}
|
|
|
|
if ( args.second().hasLength(3) )
|
|
{
|
|
ok = ( args.first() == args.second().second())
|
|
&& ((args.second().first()) == string(Symbol::MAP()) );
|
|
}
|
|
|
|
if (!ok) {
|
|
return NList::typeError("Input list has not structure " + typeA + ".");
|
|
}
|
|
mapResult = args.second().third();
|
|
break;
|
|
}
|
|
|
|
case 3: {
|
|
if(listutils::isStream(args.first().listExpr())){
|
|
return listutils::typeError("first argument cannot be a stream");
|
|
}
|
|
if(listutils::isStream(args.second().listExpr())){
|
|
return listutils::typeError("second argument cannot be a stream");
|
|
}
|
|
|
|
if ( args.third().hasLength(4) )
|
|
{
|
|
ok = (args.third().first() == string(Symbol::MAP()))
|
|
&& (args.first() == args.third().second())
|
|
&& (args.second() == args.third().third());
|
|
}
|
|
|
|
if (!ok) {
|
|
return NList::typeError("Input list has not structure " + typeB + ".");
|
|
}
|
|
mapResult = args.third().fourth();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return NList::typeError("Input list has not structure " + typeA +
|
|
" or " + typeB + ".");
|
|
}
|
|
|
|
}
|
|
|
|
if ( !(args.first() == mapResult) )
|
|
{
|
|
NList::typeError(
|
|
"Operator within expects that the first argument and the argument\n"
|
|
"of the mapping function are equal, but gets\n"
|
|
"First argument: " + args.first().convertToString() + "\n" +
|
|
"Mapping argument: " + mapResult.convertToString() + "."
|
|
);
|
|
}
|
|
|
|
} catch ( NListErr& e ) {
|
|
return NList::typeError( e.msg() );
|
|
}
|
|
// cout << ":::::::::::: " << mapResult << endl;
|
|
return mapResult.listExpr();
|
|
}
|
|
|
|
|
|
/*
|
|
2.4.3 Value mapping function of operator ~within~
|
|
|
|
*/
|
|
int
|
|
Within_s(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
switch ( message )
|
|
{
|
|
case OPEN:{
|
|
ArgVectorPointer funArgs;
|
|
funArgs = qp->Argument( args[1].addr );
|
|
(*funArgs)[0] = args[0];
|
|
qp->Open( args[1].addr );
|
|
return 0;
|
|
}
|
|
case REQUEST:{
|
|
qp->Request( args[1].addr, result );
|
|
if( qp->Received( args[1].addr ) )
|
|
return YIELD;
|
|
return CANCEL;
|
|
}
|
|
case CLOSE:{
|
|
qp->Close( args[1].addr );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Within2_s(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
switch ( message )
|
|
{
|
|
case OPEN:{
|
|
ArgVectorPointer funArgs;
|
|
funArgs = qp->Argument( args[2].addr );
|
|
(*funArgs)[0] = args[0];
|
|
(*funArgs)[1] = args[1];
|
|
qp->Open( args[2].addr );
|
|
return 0;
|
|
}
|
|
case REQUEST:{
|
|
qp->Request( args[2].addr, result );
|
|
if( qp->Received( args[2].addr ) )
|
|
return YIELD;
|
|
return CANCEL;
|
|
}
|
|
case CLOSE:{
|
|
qp->Close( args[2].addr );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
Within_o(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funArgs = qp->Argument( args[1].addr );
|
|
(*funArgs)[0] = args[0];
|
|
qp->Request( args[1].addr, result );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Within2_o(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funArgs = qp->Argument( args[2].addr );
|
|
(*funArgs)[0] = args[0];
|
|
(*funArgs)[1] = args[1];
|
|
qp->Request( args[2].addr, result );
|
|
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping withinmap[] = { Within_o, Within_s };
|
|
ValueMapping within2map[] = { Within2_o, Within2_s };
|
|
|
|
/*
|
|
2.4.2 Selection function of operator ~within~
|
|
|
|
Return values 0,2 are used for within and values 1,3 for operator within2
|
|
|
|
*/
|
|
int
|
|
WithinSelect( ListExpr Args )
|
|
{
|
|
NList args(Args);
|
|
|
|
TRACE("WithinSelect")
|
|
SHOW(args)
|
|
|
|
try {
|
|
NList map;
|
|
NList mapRes;
|
|
|
|
if ( args.length() == 2)
|
|
{ // for within
|
|
map = args.second();
|
|
mapRes = map.third();
|
|
}
|
|
else
|
|
{ // for within2
|
|
map = args.third();
|
|
mapRes = map.fourth();
|
|
}
|
|
|
|
if( mapRes.isNoAtom() && mapRes.first().str() == Symbol::STREAM() )
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
} catch ( NListErr& e ) {
|
|
|
|
NList::typeError( e.msg() );
|
|
return -1;
|
|
}
|
|
|
|
cmsg.error()
|
|
<< "Can't select value mapping function for"
|
|
<< " the overloaded operator 'within'.";
|
|
cmsg.send();
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
|
|
2.4.3 Specification of operator ~within~
|
|
|
|
*/
|
|
const string WithinSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>a x (a -> stream(b)) -> stream(b)\n"
|
|
" a x ( a -> c) -> c</text--->"
|
|
"<text>_ within [ fun ]</text--->"
|
|
"<text>Computes the first argument once. Then it calls "
|
|
"the function, passing the computed 1st argument as "
|
|
"parameter to the parameter function. This may save time, "
|
|
"if the 1st argument is referenced more than once within "
|
|
"the parameter function. The 1st argument must not be a stream!"
|
|
"</text--->"
|
|
"<text>query plz createbtree[Ort] "
|
|
"within[fun( index: ANY ) "
|
|
"Orte feed {o} loopjoin[index plz "
|
|
"exactmatch[.Ort_o]] consume]</text--->))";
|
|
|
|
const string Within2Spec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>a x b x ( a x b -> c) -> c\n"
|
|
"a x b x ( a x b -> stream(c)) -> stream(c)</text--->"
|
|
"<text>_ _ within2[ fun ]</text--->"
|
|
"<text>Computes the first and second argument once. Then it calls "
|
|
"the function, passing the two computed argument as "
|
|
"parameters to the parameter function. This may save time, "
|
|
"if the 1st argument an/or 2nd arguments are referenced more than once "
|
|
"within the parameter function. The first 2 arguments must not be streams!"
|
|
"</text--->"
|
|
"<text>(1+2) (2+3) within2[fun(I1: ANY, I2: ANY2) I1 + I2]</text--->))";
|
|
|
|
/*
|
|
2.4.3 Definition of operator ~within~, ~within2~
|
|
|
|
*/
|
|
Operator within (
|
|
"within", // name
|
|
WithinSpec, // specification
|
|
2, // the number of overloaded functions
|
|
withinmap, // value mapping function array
|
|
WithinSelect, // the selection function
|
|
WithinTypeMap // type mapping
|
|
);
|
|
|
|
Operator within2 (
|
|
"within2", // name
|
|
Within2Spec, // specification
|
|
2, // the number of overloaded functions
|
|
within2map, // value mapping function array
|
|
WithinSelect, // the selection function
|
|
WithinTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.5 Operator ~whiledo~
|
|
|
|
Performs some while-loop like iteration on an object.
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
2.5.1 Type Mapping for ~whiledo~
|
|
|
|
----
|
|
T x (T --> bool) x (T --> T) [x bool] --> stream(T)
|
|
where T in kind DATA or T = tuple(X)
|
|
----
|
|
|
|
*/
|
|
|
|
ListExpr WhileDoTypeMap(ListExpr Args)
|
|
{
|
|
|
|
NList args(Args);
|
|
static const string typeA =
|
|
"(T (map T bool) (map T T)), where T in DATA or T = tuple(X)";
|
|
ListExpr errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR"));
|
|
|
|
int noargs = args.length();
|
|
|
|
if((noargs == 4) &&
|
|
!(listutils::isSymbol(nl->Fourth(Args),CcBool::BasicType()))){
|
|
return listutils::typeError("Optional 4th parameter "
|
|
"must be of type 'bool'.");
|
|
}
|
|
if( (noargs == 3 || noargs == 4)
|
|
// second argument
|
|
&& args.second().hasLength(3)
|
|
&& args.second().first() == Symbols::MAP()
|
|
&& args.second().second() == args.first()
|
|
&& args.second().third() == Symbols::BOOL()
|
|
// third argument
|
|
&& args.third().hasLength(3)
|
|
&& args.third().first() == Symbols::MAP()
|
|
&& args.third().second() == args.first()
|
|
&& args.third().third() == args.first()
|
|
)
|
|
{
|
|
if( // first argument
|
|
SecondoSystem::GetAlgebraManager()
|
|
->CheckKind(Kind::DATA(), args.first().listExpr(), errorInfo)
|
|
)
|
|
{ // case: T x (T --> bool) x (T --> T) --> T, where T in DATA
|
|
if(noargs == 4 && args.fourth() == Symbols::BOOL() ) {
|
|
NList restype(Symbols::STREAM(), args.first());
|
|
return restype.listExpr();
|
|
} else { // append 4th argument bool = TRUE
|
|
NList restype(Symbols::APPEND(),
|
|
NList(false,false),
|
|
NList(Symbols::STREAM(), args.first()));
|
|
return restype.listExpr();
|
|
}
|
|
}
|
|
else if(
|
|
args.first().hasLength(2)
|
|
&& args.first().first() == Symbols::TUPLE()
|
|
&& (args.first().second().length() > 0)
|
|
// Only the first attribute is checked!
|
|
&& args.first().second().first().hasLength(2)
|
|
&& args.first().second().first().first().isSymbol()
|
|
&& SecondoSystem::GetAlgebraManager()
|
|
->CheckKind(Kind::DATA(),
|
|
args.first().second().first().first().listExpr(),
|
|
errorInfo
|
|
)
|
|
)
|
|
{ // case: T x (T --> bool) x (T --> T) --> T, where T = tuple(X)
|
|
// NList restype(Symbols::STREAM(), args.first());
|
|
// return restype.listExpr();
|
|
return NList::typeError("WhileDoTypeMap: tuple(X) x (tuple(x) --> bool) "
|
|
"x (tuple(x) --> tuple(x)) --> tuple(x) still not implemented!");
|
|
}
|
|
// else: error
|
|
}
|
|
// else: error
|
|
return NList::typeError("Expected " + typeA + ".");
|
|
}
|
|
|
|
/*
|
|
2.5.2 Value Mapping for ~whiledo~
|
|
|
|
*/
|
|
|
|
struct WhileDoValueMapLocalInfo{
|
|
Word lastInstance;
|
|
Word pred;
|
|
Word fun;
|
|
bool avoidEndlessLoop;
|
|
bool finished;
|
|
bool isInitial;
|
|
};
|
|
|
|
// version for DATA object -> stream(object)
|
|
int WhileDoValueMap(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
Word predResult, funResult;
|
|
ArgVectorPointer predArg, funArg;
|
|
WhileDoValueMapLocalInfo* sli = 0;
|
|
CcBool *avoidEndless = static_cast<CcBool*>(args[3].addr);
|
|
|
|
switch (message)
|
|
{
|
|
case OPEN :
|
|
sli = new WhileDoValueMapLocalInfo;
|
|
sli->lastInstance.setAddr(((Attribute*)(args[0].addr))->Copy());
|
|
sli->pred = args[1];
|
|
sli->fun = args[2];
|
|
sli->finished = false;
|
|
sli->isInitial = true;
|
|
|
|
if(!avoidEndless->IsDefined()){
|
|
cmsg.error() << "WARNING: "<< __PRETTY_FUNCTION__
|
|
<< ": Optional bool parameter is UNDEFINED! Using TRUE."
|
|
<< endl;
|
|
cmsg.send();
|
|
sli->avoidEndlessLoop = true;
|
|
} else {
|
|
sli->avoidEndlessLoop = avoidEndless->GetBoolval();
|
|
}
|
|
local.setAddr(sli);
|
|
return 0;
|
|
|
|
case REQUEST :
|
|
|
|
if( local.addr == 0 )
|
|
{
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
sli = (WhileDoValueMapLocalInfo*)local.addr;
|
|
|
|
if(sli->finished)
|
|
{
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
// At first request, forward a copy of the initial instance
|
|
if(sli->isInitial)
|
|
{
|
|
result.setAddr(((Attribute*)(sli->lastInstance.addr))->Clone());
|
|
sli->isInitial = false;
|
|
return YIELD;
|
|
}
|
|
// For each REQUEST, we check if we have already finished,
|
|
// If not, we pass the last instance to the parameter function
|
|
// and evalute the latter.
|
|
// If the new instance is the same as the last one. we have reached a
|
|
// fixpoint set finished, and return the result (so that the user may
|
|
// notice that a fixpoint has been reached).
|
|
// Otherwise, the new instance is passed to the parameter predicate
|
|
// function. If it evaluates to UNDEF or FALSE, we set finished,
|
|
// otherwise not.
|
|
predArg = qp->Argument(sli->pred.addr); // set argument for the
|
|
(*predArg)[0] = sli->lastInstance; // parameter predicate
|
|
qp->Request(sli->pred.addr, predResult); // call predicate function
|
|
if( !(static_cast<CcBool*>(predResult.addr)->IsDefined()) )
|
|
{ // UNDEF pred result
|
|
sli->finished = true;
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
sli->finished = !(static_cast<CcBool*>(predResult.addr)->GetBoolval());
|
|
if(sli->finished)
|
|
{ // Does predicate hold on last instance?
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
// Create the next instance
|
|
funArg = qp->Argument(sli->fun.addr); // set argument for the
|
|
(*funArg)[0] = sli->lastInstance; // parameter function
|
|
qp->Request(sli->fun.addr, funResult); // call parameter function
|
|
// copy result (and hand it over to the out-stream):
|
|
result.setAddr(((Attribute*) (funResult.addr))->Clone());
|
|
if ( sli->avoidEndlessLoop
|
|
&& (((Attribute*) (funResult.addr))
|
|
->Compare(((Attribute*) (sli->lastInstance.addr))) == 0)
|
|
)
|
|
{ // reached a fixpoint: changeing sli->lastInstance not required
|
|
sli->finished = true;
|
|
return YIELD;
|
|
}
|
|
((Attribute*) (sli->lastInstance.addr))->DeleteIfAllowed();
|
|
(sli->lastInstance).setAddr(((Attribute*)(result.addr))->Copy());
|
|
return YIELD;
|
|
|
|
case CLOSE :
|
|
|
|
if( local.addr != 0 )
|
|
{
|
|
sli = (WhileDoValueMapLocalInfo*)local.addr;
|
|
((Attribute*) (sli->lastInstance.addr))->DeleteIfAllowed();
|
|
delete sli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
} // end switch
|
|
cmsg.error() << "WhileDoValueMap received UNKNOWN COMMAND" << endl;
|
|
cmsg.send();
|
|
return -1; // should not be reached
|
|
}
|
|
|
|
/*
|
|
2.5.1 Type Mapping for ~whiledo~
|
|
|
|
*/
|
|
|
|
|
|
const string WhileDoSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>T x (T -> bool) x (T -> T) x bool -> stream(T), "
|
|
"where T in kind DATA\n"
|
|
"tuple(T) x (tuple(T) -> bool) x (tuple(T) x bool -> tuple(T)) -> "
|
|
"stream(tuple(T))</text--->"
|
|
"<text>obj whiledo[ pred ; func ; avoidEndless ]</text--->"
|
|
"<text>Always copies the first parameter into the result stream. Then, "
|
|
"it copies 'obj' to its internal loop variable and starts a pre-check "
|
|
"loop: as long as 'pred' evaluates to TRUE on the loop variable, function "
|
|
"'func' is evaluated for the current loop variable. "
|
|
"Each result is copied into the result stream. If avoidEndless = TRUE and "
|
|
"a fixpoint is reached during the evaluation (loop variable does not "
|
|
"change), the processing is stopped. In this case, the two last results "
|
|
"will be identical. If 'pred' evaluates to UNDEF, the iteration stops "
|
|
"immediately (without creating any further result objects)."
|
|
"If 'avoidEndless' is specified, but UNDEF, this is handled as if it was "
|
|
"TRUE.</text--->"
|
|
"<text>query 1 whiledo[ . < 10 ; . + 1 ; TRUE] count"
|
|
"</text--->))";
|
|
|
|
|
|
Operator whiledo (
|
|
"whiledo", // name
|
|
WhileDoSpec, // specification
|
|
WhileDoValueMap, // value mapping function array
|
|
Operator::SimpleSelect, // the selection function
|
|
WhileDoTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
Type Map Operator ARGX
|
|
|
|
*/
|
|
template<int X>
|
|
ListExpr ARGEFXTM(ListExpr args){
|
|
|
|
for(int i=0;i<X-1 && nl->HasMinLength(args,2) ;i++){
|
|
args = nl->Rest(args);
|
|
}
|
|
|
|
ListExpr res = nl->First(args);
|
|
if(listutils::isAnyMap(res)){
|
|
// switch to result
|
|
while(!nl->HasLength(res,1)){
|
|
res = nl->Rest(res);
|
|
}
|
|
res = nl->First(res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
OperatorSpec ARGEFXSpec(
|
|
" A x B x C ... -> C",
|
|
" ARGX(X,B,C)",
|
|
" ARGEFX extract the Xth argument type from the input. If it is a "
|
|
"funtion, the result of this function is returned",
|
|
" query xyz"
|
|
);
|
|
|
|
Operator ARGEF1Op(
|
|
"ARGEF1",
|
|
ARGEFXSpec.getStr(),
|
|
0,
|
|
Operator::SimpleSelect,
|
|
ARGEFXTM<1>
|
|
);
|
|
|
|
Operator ARGEF2Op(
|
|
"ARGEF2",
|
|
ARGEFXSpec.getStr(),
|
|
0,
|
|
Operator::SimpleSelect,
|
|
ARGEFXTM<2>
|
|
);
|
|
|
|
Operator ARGEF3Op(
|
|
"ARGEF3",
|
|
ARGEFXSpec.getStr(),
|
|
0,
|
|
Operator::SimpleSelect,
|
|
ARGEFXTM<3>
|
|
);
|
|
|
|
|
|
Operator ARGEF4Op(
|
|
"ARGEF4",
|
|
ARGEFXSpec.getStr(),
|
|
0,
|
|
Operator::SimpleSelect,
|
|
ARGEFXTM<4>
|
|
);
|
|
|
|
Operator ARGEF5Op(
|
|
"ARGEF5",
|
|
ARGEFXSpec.getStr(),
|
|
0,
|
|
Operator::SimpleSelect,
|
|
ARGEFXTM<5>
|
|
);
|
|
|
|
Operator ARGEF6Op(
|
|
"ARGEF6",
|
|
ARGEFXSpec.getStr(),
|
|
0,
|
|
Operator::SimpleSelect,
|
|
ARGEFXTM<6>
|
|
);
|
|
|
|
|
|
/*
|
|
2.5 Operator funseq
|
|
|
|
This operator gets some value and a sequence of functions.
|
|
The first function processes the value, the second function processes
|
|
the value as well as the result of the first funtion, the third function processes
|
|
the value, the result of the first and the result of the second function and so on.
|
|
The number of functions is restricted to 7.
|
|
|
|
*/
|
|
template<int nofuns>
|
|
ListExpr funseqTM(ListExpr args){
|
|
|
|
if(!nl->HasLength(args,nofuns+1)){
|
|
return listutils::typeError("ANY x funlist expected");
|
|
}
|
|
vector<ListExpr> funargs;
|
|
funargs.push_back(nl->First(args)); // just overtak the initial value
|
|
ListExpr funs = nl->Rest(args);
|
|
if(nl->AtomType(funs) != NoAtom){
|
|
return listutils::typeError("second argument must be a list of functions");
|
|
}
|
|
if(!nl->HasLength(funs,nofuns)){
|
|
return listutils::typeError("invalid number of functions");
|
|
}
|
|
while(!nl->IsEmpty(funs)){
|
|
ListExpr fun = nl->First(funs);
|
|
funs = nl->Rest(funs);
|
|
if(!listutils::isAnyMap(fun)){
|
|
return listutils::typeError("found a non-funtion in function list");
|
|
}
|
|
int noFunArgs = nl->ListLength(fun) -2 ; // map and result
|
|
if(noFunArgs<(int)funargs.size() ){
|
|
return listutils::typeError("found function with too less arguments");
|
|
}
|
|
if(noFunArgs> nofuns){
|
|
return listutils::typeError("found function with too much arguments");
|
|
}
|
|
// check arguments types
|
|
fun = nl->Rest(fun); // remove leading map
|
|
for(size_t a = 0; a < funargs.size(); a++){
|
|
if(!nl->Equal(nl->First(fun),funargs[a])){
|
|
return listutils::typeError("wrong funtion argument found");
|
|
}
|
|
fun = nl->Rest(fun);
|
|
}
|
|
// remaining arguemnts are filled up with the last argument
|
|
while(!nl->HasLength(fun,1)){
|
|
if(!nl->Equal(nl->First(fun),funargs.back())){
|
|
return listutils::typeError("wrong funtion argument found");
|
|
}
|
|
fun = nl->Rest(fun);
|
|
}
|
|
funargs.push_back(nl->First(fun));
|
|
}
|
|
ListExpr result = funargs.back();
|
|
return result;
|
|
}
|
|
|
|
|
|
int
|
|
funseqVM(Word* args, Word& result, int message, Word& local, Supplier s){
|
|
int noArgs = qp->GetNoSons(s);
|
|
vector<Word> arguments;
|
|
// the first arguments is just the argument given in the to the operator
|
|
arguments.push_back(args[0]);
|
|
for(int f = 1;f<noArgs;f++){
|
|
Word fun = args[f];
|
|
ArgVectorPointer funargs = qp->Argument(fun.addr);
|
|
for(size_t fa = 0; fa < arguments.size();fa++){
|
|
(*funargs)[fa] = arguments[fa];
|
|
}
|
|
for(int fa = arguments.size(); fa < noArgs; fa++){
|
|
(*funargs)[fa] = arguments.back();
|
|
}
|
|
Word funresult;
|
|
qp->Request(fun.addr, funresult);
|
|
arguments.push_back(funresult);
|
|
}
|
|
qp->DeleteResultStorage(s);
|
|
qp->ChangeResultStorage(s,arguments.back());
|
|
result = qp->ResultStorage(s);
|
|
qp->ReInitResultStorage(qp->GetSon(s,noArgs-1));
|
|
return 0;
|
|
}
|
|
|
|
OperatorSpec funseqSpec(
|
|
"A x (A -> X) x (A x X -> Y) x (A x X x Y -> Z) ... -> Z",
|
|
" A funsq [funlist] ",
|
|
"Applies a set of function to an object. Each function can "
|
|
"use the initial argument and the result of all functions "
|
|
"on it's left side. Function result cannot be streams.",
|
|
"query 1 funseq[ fun( a : int) a + 1.0, fun (b : int, c : real) b * c]"
|
|
);
|
|
|
|
Operator funseq1Op(
|
|
"funseq1",
|
|
funseqSpec.getStr(),
|
|
funseqVM,
|
|
Operator::SimpleSelect,
|
|
funseqTM<1>
|
|
);
|
|
|
|
Operator funseq2Op(
|
|
"funseq2",
|
|
funseqSpec.getStr(),
|
|
funseqVM,
|
|
Operator::SimpleSelect,
|
|
funseqTM<2>
|
|
);
|
|
|
|
Operator funseq3Op(
|
|
"funseq3",
|
|
funseqSpec.getStr(),
|
|
funseqVM,
|
|
Operator::SimpleSelect,
|
|
funseqTM<3>
|
|
);
|
|
|
|
Operator funseq4Op(
|
|
"funseq4",
|
|
funseqSpec.getStr(),
|
|
funseqVM,
|
|
Operator::SimpleSelect,
|
|
funseqTM<4>
|
|
);
|
|
|
|
Operator funseq5Op(
|
|
"funseq5",
|
|
funseqSpec.getStr(),
|
|
funseqVM,
|
|
Operator::SimpleSelect,
|
|
funseqTM<5>
|
|
);
|
|
|
|
Operator funseq6Op(
|
|
"funseq6",
|
|
funseqSpec.getStr(),
|
|
funseqVM,
|
|
Operator::SimpleSelect,
|
|
funseqTM<6>
|
|
);
|
|
|
|
|
|
/*
|
|
3 Creating the Algebra
|
|
|
|
*/
|
|
class FunctionAlgebra : public Algebra
|
|
{
|
|
public:
|
|
FunctionAlgebra() : Algebra()
|
|
{
|
|
AddTypeConstructor( &functionMap );
|
|
AddOperator( &ANY );
|
|
AddOperator( &ANY2 );
|
|
AddOperator( &within );
|
|
AddOperator( &within2 );
|
|
AddOperator( &whiledo );
|
|
AddOperator( &ARGEF1Op );
|
|
AddOperator( &ARGEF2Op );
|
|
AddOperator( &ARGEF3Op );
|
|
AddOperator( &ARGEF4Op );
|
|
AddOperator( &ARGEF5Op );
|
|
AddOperator( &ARGEF6Op );
|
|
AddOperator( &funseq1Op );
|
|
AddOperator( &funseq2Op );
|
|
AddOperator( &funseq3Op );
|
|
AddOperator( &funseq4Op );
|
|
AddOperator( &funseq5Op );
|
|
AddOperator( &funseq6Op );
|
|
}
|
|
~FunctionAlgebra() {};
|
|
};
|
|
|
|
|
|
/*
|
|
4 Initialization
|
|
|
|
Each algebra module needs an initialization function. The algebra manager
|
|
has a reference to this function if this algebra is included in the list
|
|
of required algebras, thus forcing the linker to include this module.
|
|
|
|
The algebra manager invokes this function to get a reference to the instance
|
|
of the algebra class and to provide references to the global nested list
|
|
container (used to store constructor, type, operator and object information)
|
|
and to the query processor.
|
|
|
|
The function has a C interface to make it possible to load the algebra
|
|
dynamically at runtime.
|
|
|
|
*/
|
|
|
|
extern "C"
|
|
Algebra*
|
|
InitializeFunctionAlgebra( NestedList* nlRef,
|
|
QueryProcessor* qpRef,
|
|
AlgebraManager* amRef )
|
|
{
|
|
nl = nlRef;
|
|
qp = qpRef;
|
|
am = amRef;
|
|
return (new FunctionAlgebra());
|
|
}
|
|
|
|
} // end of namespace FunctionAlgebra
|