Skip to content

Namespaces

Positron edited this page Aug 11, 2017 · 3 revisions

Namespaces in BCS work similar to namespaces in other languages like C++ and C#. The . operator is used to access the objects of a namespace:

namespace Test {
   int v = 123;
   void F() { Print( d: v ); }
   enum { C = 321 };
}

script "Main" open {
   Test.v = Test.C;
   Test.F(); // Output: 321
}

A nested namespace can be declared in one go:

// These are the same:
namespace A { namespace B { namespace C {} } }
namespace A.B.C {}

To avoid confusion with global variables, the global namespace is called the upmost namespace. The upmost keyword refers to the upmost namespace:

int a = 123;
namespace Test {
   int a = 321;
   script "Main" open {
      Print( d: a ); // Output: 321
      Print( d: upmost.a ); // Output: 123
   }
}

In a namespace, the magic identifier, __NAMESPACE__, is present. __NAMESPACE__ is a string that contains the full name of the namespace where __NAMESPACE__ is used. The upmost namespace does not have an actual name, so when __NAMESPACE__ is used in the upmost namespace, the empty string ("") is returned.

The following example uses __NAMESPACE__. Note that the string returned is in lowercase because identifiers in ACS and BCS get lowercased during compilation.

namespace A.B.C {
   script 1 open {
      Print( s: __NAMESPACE__ ); // Output: a.b.c
   }
}

script 2 open {
   Print( s: __NAMESPACE__ ); // Output:
}

Strict behavior

The strict qualifier enables strong types. It tells the compiler not to implicitly cast values of a primitive type to raw:

strict namespace Test {
   script "Main" open {
      int value1 = "abc"; // Error: initializer of wrong type.
      str value2 = "abc"; // All good.
   }
}

The strict qualifier also enables block scoping. For every local declaration, the let keyword is implied, so you don't need to specify it:

strict namespace Test {
   script "Main" open {
      int var = 123;
      {
         int var = 321;
      }
   }
}

If you don't want to use namespaces but still want strong types and block scoping, you can wrap your code in a nameless namespace block and qualify it with strict. The nameless namespace block will have the name of the parent namespace block; if there is no parent block, the nameless namespace block will be a part of the upmost namespace:

// This namespace block is part of the upmost namespace.
strict namespace {
   // Value and variable must have the same type:
   int a = ( int ) "abc";
   script "Main" open {
      // Block scoping enabled by default. No need for the `let` keyword:
      for ( int i = 0; i < 10; ++i ) {}
      for ( int i = 0; i < 10; ++i ) {}
   }
}

Importing stuff

The using directive is used to import objects from a namespace. You can either import a whole namespace or import specific objects from a namespace.

Importing namespaces
using namespace ;

You can import a whole namespace. This will make all of the objects in the specified namespace available for use:

namespace Test {
   int v = 123;
   void F() { Print( d: v ); }
   enum { C = 321 };
}

// All of the objects in the `Test` namespace will now be available.
using Test;

script "Main" open {
   v = C;
   F();
}
Importing specific objects
using namespace : [enum|struct] [alias =] object [, ...] ;

If you need only a few objects from a namespace, you can import only those objects you want. You can give the imported object a different name if you like:

namespace Test {
   int v = 123;
   void F() { Print( d: v ); }
   enum { C = 321 };
}

// Import only `v` and `C` from the `Test` namespace. `C` is referred to as
// `CONSTANT`.
using Test: v, CONSTANT = C;

script "Main" open {
   v = CONSTANT;
   Test.F(); // Output: 321
}