Compile-time entities

The following example illustrates the various compile-time entities in an SPL file:
namespace com.teracloud.sample.myNameSpace;                                          //1
use com.teracloud.anotherNameSpace::*;                                               //2
type T = int32;                                                                //3
T mySPLFunction(T x) { return 2 * x; }                                         //4
void myOtherFunction(mutable list<T> a) { a = [99]; }                          //5
public composite SourceSink {                                                  //6
  type   MyType = int32 i, int32 j;                                            //7
  graph  stream<MyType> B = FileSource() { param file: "a.dat"; }              //8
         stream<B> C = SomeOperator(B) { param someParam: sqrt(i) + abs(j); }  //9
  config logLevel : trace;                                                     //10
}                                                                              //11
Figure 1. Compile-time entities

A toolkit (directory) can contain one or more namespaces (subdirectory). Each namespace can contain XML model files for native functions and primitive operators, and SPL compilation units (files). Each compilation unit can contain top-level definitions for types, functions, and composites. Each composite operator definition can contain a type clause with type definitions and a graph clause with operator invocations.

Each SPL file belongs to a namespace (Line 1). The use directive makes entities from other namespaces available under a simple name (Line 2). For example, instead of com.ibm.anotherNameSpace::SomeOperator, you can simply write SomeOperator (Line 9). An SPL file contains type definitions (Line 3), function definitions (Lines 4-5), and composite operator definitions (Lines 6-12). A composite operator can contain a clause with type definitions (Line 7). Streams are always defined in the graph clause of some composite operator (Lines 8-9) to encourage reusable code and provide a layer for modularization. The figure shows how all these compile-time entities fit together. The start symbol for parsing an SPL file is the compilation unit:

compilationUnit ::= namespace? useDirective* 
                    (compositeDef | functionDef | standAloneTypeDef)*
namespace       ::= 'namespace' ID+. ';'
useDirective    ::= 'use' ID+. '::' ( '*' | ID ) ';'

A namespace contains all the operators, types, and functions that are defined in files that belong to the namespace. Thus, a namespace in SPL is similar to a package in Java. Like in Java and unlike in C++, there are no partial qualifiers, that is, if the current namespace is a.b, access to members of namespace a.b.c cannot use just the suffix c as a qualifier. However, unlike in Java, namespace qualifiers use :: instead of . to distinguish them from operator member access. For example: my.name.space::myOperator.myType. A namespace can also contain primitive operators or native functions. They are not defined in SPL, but can be invoked from SPL. For example, Line 9 invokes an operator SomeOperator, which can either be a composite operator from another SPL file, or a primitive operator declared in a primitive operator model file. Similarly, Line 9 invokes two functions sqrt and abs, which can either be non-native functions from another SPL file, or native functions declared in an XML function model file.

Functions, types, and operators can be used by their simple name if they are either part of the same namespace, or made available by a use directive. Examples for simple names include T (Line 5), FileSource (Line 8), and sqrt (Line 9). The use directive comes in two forms (similar to Java); it can either refer to a single entity by name, or all entities in a namespace by using the wildcard *. For example:
namespace test;                             //1
use your.name.space1::YourOperatorOrType;   //2
use your.name.space1::yourFunction;         //3
use your.name.space2::*;                    //4
public composite Comp { /*...*/ }           //5

All files have an implicit wildcard use directive for the default namespaces spl, spl.adapter, spl.relational, and spl.utility.

Tip: If a composite operator is used across files, it is good style, though not mandatory, to declare it in a file with the same name. For example, define theMyOp operator in MyOp.spl if it gets used from other files.