UIEvolutin Inc.
UJML Language Reference
Scope

Scope is where identifiers are valid in your UJML code.

When you refer to an identifier in your code, UJML has to 'resolve' the identifier name back to what it refers to. If UJML consisted only of application files containing one set of variables with one set of transitions and one set of templates, all controlled by one script, then resolving names would be easy; after all, each thing would have its own name and duplicate names would be an error. 

But UJML is not that simple. In fact, if UJML were that simple you couldn't do very much with it. So there has to be a way to resolve names that deals with the possibility of two things with the same name. This is where the concept of a 'scope' comes in—with the simple rule that, within a particular scope, each name may only refer to one thing.

Definition of scope

The scope of an identifier in UJML refers to the places in your code where that identifier is valid. A scope is an area of code where identifiers may be declared and used separately. A 'child' scope is an area of code, completely contained by a 'parent' scope, which has its own scope. For example an application, partition, or state machine is a parent scope and a function within that parent scope is a child scope.

Types of scope
Name 
Description 
Local 
Locally-scoped identifiers are available to code in the same scope where they are declared. 
Module 
Module-scoped identifiers are declared inside of an application or partition and are available to code in that module and to code in state machines, functions, transitions, or events (child scopes) of that module. 
Shared-scoped identifiers are module-scoped items which are imported into or exported from that module. Shared scope identifiers are available to code in both the module importing it and the module exporting it. See Sharing
Global 
Globally-scoped identifiers are available everywhere. UJML does not explicitly support global scope; at best you can make certain to use the same shared identifiers in all modules of an application.
Note: The use of global variables is generally considered a bad practice. 
Determining an identifier's scope

An identifier's scope is determined by the place in code where it is declared. An identifier declared in an application, partition, component, or state machine has both local- and module-level scope (meaning that it has local scope within the module and module scope within child scopes of the module). An identifier declared in a function, transition, or event can only have local scope. An identifier declared in a component 

Except for identifiers in components, identifiers with module-level scope may be shared with other modules using the sharing declaration modifiers 'access' and 'visibility'. See Sharing. Identifiers may be used outside of the place they were declared (the 'local' scope) only when they are explicitly shared or when they are referenced by a child scope.

Identifiers in components

Identifiers in components are scoped at the module level of the component and to any child scopes of the component, but are private to the component (with the exception of public functions implementing interface methods). See Components, Interfaces

Note: Components can load partitions in the same way applications and partitions do. For a loaded partition the component instance is the top level scope.

Name resolution

UJML resolves names by first looking for an identifier in the current scope. If the current scope is a child scope and the identifier does not exist in that local scope, UJML will next look in the module and shared scopes for the identifier. 

Duplicate identifiers may exist in both the local scope and the parent scope. In this case, the identifier is resolved to the local scope because resolution occurs there first. Duplicate identifiers may not exist at the same level of scope.

Namespaces and namespace hierarchies

A namespace in UJML is a single scope and the names defined within that scope. For example, the namespace of a function is the function's local scope, including any parameters or variables defined within that function. As described above, scopes may be child scopes of a parent scope; in which case, name resolution also includes the namespace in the parent's scope. This parent/child relationship is also known as a 'namespace hierarchy'. 

If the parent itself is the child of another scope, this name resolution process may continue upwards until a matching identifier is found in the namespace hierarchy. In UJML, this occurs with state machines because a state machine may contain other state machines. This means that the namespace hierarchy of a state machine includes its parent state machine and its parent's parent and so on, all the way up until the module-level scope is reached. See State Machines

This means that UJML code inside of a state machine nested to any depth has access to identifiers at any level in the hierarchy above it, following normal scoping rules.

The following example shows how to declare some variables with module-level scope. It is part of the helloworld4.ujml sample.

<variables>
    <var name="mPosX" type="int"/>
    <var name="mPosY" type="int"/>
    <var name="mWidth" type="int"/>
    <var name="mHeight" type="int"/>
</variables>

 

The following example is an event that declares a locally-scoped variable and calls a module-scoped function. It is part of the events.ujml sample.

<event name="onKeyDown">
    <variables>
        <var name="key" type="string"/>
    </variables>
    <script>
        key = _getStringEventData(&_EVENT_STRING_ONKEY;);
        showMessage(_strcat("onKeyDown: ", key));
    </script>
</event>

 

The following example is a function that shows how to declare a locally-scoped variable, but also uses module-level scoped variables. The identifier for the function itself has module-level scope, so the function may be called from anywhere within the same application. It is part of the events.ujml sample. The datetime.ujml sample has a similar function which is contained by a state machine.

<function name="showMessage" type="void">
    <parameters>
        <var name="message" type="string"/>
    </parameters>
    <variables>
        <var name="ctr" type="int"/>
    </variables>
    <script>
        // Shift old messages up and add current message.
        for (ctr = &MESSAGE_COUNT; - 1; ctr &_GT; 0; ctr--)
        {
            mOldMessageLines[ctr] = mOldMessageLines[ctr - 1];
        }
        mOldMessageLines[0] = mCurrMessageLine;

        // Show old messages.
        for (ctr = 0;
            messageLineY(ctr) &_GTE; 0 &_AND; ctr &_LT; &MESSAGE_COUNT; ;
            ctr++)
        {
            if (_streq(mOldMessageLines[ctr], ""))
            {
                sOldMessageLines[ctr] = false;
            }
            else
            {
                _clear_state(sOldMessageLines[ctr]);
                sOldMessageLines[ctr] = true;
            }
        }

        // Show new message.
        mCurrMessageLine = message;
        _clear_state(sCurrMessageLine);
        sCurrMessageLine = true;

        // Send new message to trace display.
        _trace(message);
    </script>
</function>
Copyright (c) 2000-2007 UIEvolution, Inc. All rights reserved.
What do you think about this topic? Send feedback!