Inq Hierarchical Data Structures
The Containment Hierarchy
Inq uses the recursively defined data structure of a node that can contain any number of named child nodes to represent all aspects of an application. All entities, whether data, algorithms, user interface components, indeed anything accessible from the Inq language are represented by such nodes. The exact type of a node, and therefore the operations that can be performed on it, is not known until runtime.
Nodes that can contain named children are Maps, that is, such a node yields a chosen child by the application of some key.
Node Paths
To address a particular node a node path is applied from some starting point. The simplest type of node path takes the form
foo.bar
A path consists of a number of elements, each separated by a delimiter. In the above example the elements are Inq identifiers and the delimiter is the period character.

When applied to the structure shown in the diagram the result is the string "Hello, World".
If a path does not resolve then its result is unresolved null (discussed further when considering the null constant).
The Context Node
An Inq process environment provides a root node under which all aspects of the application running within that process are built. Inq script runs either as a service request made between two Inq environments or as a callback in a client GUI and is executed against a specific node, known as the context node. This is analogous to the concept of a 'current working directory'.

The context node is the node from which all path specifications qualified with the $context prefix will be applied. In the above diagram, for example, if the context node were specified by the path p.q.x then the context node would be node4. A context node typically defines the root of a particular sub-division of an application. Two different contexts may hold similar items beneath them, if they represent two instances of, say, a Customer Editor tool.
Unless script has entered a foreach loop body, or when applying expression arguments to functions such as sort(), the context node is also returned by the path $this. It is conventional to use $context only in cases where $this is not also the context. Otherwise, $this is preferred.
Because the context node is a descendent of a process's root node, any structure built there remains accessible across service requests (or GUI callbacks), and for the lifetime of the process until wilfully removed.
The Stack
When Inq script runs a call stack is established. The stack is initialized with any arguments passed to the service and subsequent stack frames are created as functions are called.
The stack can be referenced with paths of the form $stack.p.q but because the stack is referenced most often it is the default starting point for path resolution and the $stack. prefix is optional.
Stack frames are unwound as functions return to their caller. Anything built on the stack becomes unreferenced once its frame is unwound.
Path Prefices
If a path contains no special prefix then it is applied relative to the stack. However, there are a number of prefix tokens that have the following meanings
| Token | Meaning |
|---|---|
| $this | Resolves to the current context node unless within a loop or executing a function with an implied iteration. |
| $root | Resolves to the process's root node. |
| $stack | Resolves to the current stack frame. This is the default. |
| $process | Resolves to the executing process. |
| $context | Resolves to the context node. |
| $path | Resolves to a string which is the path specification to the node given by $context. |
| $catalog | Resolves to the global catalog available to all processes. |
| $properties | Provides access to the JavaTM system properties |
| $uidefaults | Provides access to the User Interface default property settings. |
Identifiers and Reserved Words
Inq has a significant number of reserved words arising from its syntax and built-in functions. A path containing more than one element is always recognised as such because the presence of delimiters remove any ambiguity, however single-element, stack-assumed paths will generate an error if they use a reserved word. This can be resolved by the (benign) prefix of the period character. For example, Inq supports a length function to return the length of a string. The following paths are legal:
vars.length; .length;
However this is not:
length;
Complex Path Formats
Inq supports a syntax in node paths for substitution of path elements and node access by vector.
Path Element Substitution
A simple path specification is appropriate where a node path is known in advance and does not require run-time substitution. Under some circumstances, however, it is desirable to evaluate one or more components of a path specification as the result of other path references. Paths of this nature take the form
a.{p.q.r}.c;
A path element contained within braces is itself evaluated and the result used as the key applied at the current step in path resolution. In the example, the result of the path p.q.r is applied to the node at a to yield the appropriate child node. This node, in turn, then has the path c applied to it.
If the path p.q.r resolves to a path() (see below) then its elements are used to continue the resolution. As a convenience, if a string is yielded then it is tokenized into individual path elements. In the above example, if p.q.r resolves to x.y.z then the effective node specification overall is a.x.y.z.c
The substitution does not have to result in a path or a string and a result of any other type is applied as a single path component. An example of the use of this type of substitution is given when discussing building node structures.
Vector Access
If a map is orderable then its children can be accessed by a zero-based vector index as well as by name. The syntax for such a path is as follows
customers[3].ContactDetails.Fax;
The square bracket can contain either
- the literal string first
- the literal string last
- a literal integer, (0 is not equivalent to first, see below)
- a node path that resolves to an integer
An index that is out of range generates an exception. This includes the value zero when the vector is empty, however sometimes it is desirable to allow this case to result in unresolved null. Applying a path of the form customers[first] results in the zero-th vector entry or unresolved null when the vector is empty:
any firstEntry = customers[first];
if (firstEntry)
{
// Do something with firstEntry
}
Using last is a more succinct and efficient expression of
any entries = count(customers) - 1; customers[entries]
As well as ordered maps, Inq also supports arrays which offer vector access only. The capabilities of the various container nodes available are discussed in the section on Container Types. A run-time error occurs if the node at which the vector access is applied does not support vector addressing.
Lazy Evaluation
Path elements may be delimited with the * character, which indicates lazy evaluation of the node path. In this case, if the current node does not contain the following element a breadth-wise iteration is performed to search for it. Referring to the above diagram, if a path of p.q*s is applied starting from node1 it is undefined whether node7 or node9 would be returned.
Lazy evaluation can be used to address nodes, however it is more often applied when specifying paths as a way of discriminating events. This is covered when discussing how Inq scripts can explicitly listen for events.
The path Function
Certain Inq functions require a path argument, rather than the node it resolves to. The path function protects a path from being resolved:
path(p.q.r);
If the argument contains substitutions then path attempts to resolve them. Those that succeed are placed in the resulting path, however any that fail remain unresolved in the path until it is applied somehow. Vector access is always resolved every time a path is applied. Here are some examples in pathtests.inq that illustrate these outcomes. Output is interspersed where appropriate:
// Set up some node structure to test our paths against.
// First, create an ordered map (so we can show vector access as well)
omap g.h;
// Add some nodes. Use numbers that make vector tests obvious
// (ok vectors start from zero but you know...)
int g.h.a1.n = 11;
int g.h.a2.n = 22;
int g.h.a3.n = 33;
// Value to test vector access with. Should result in node a3
int m = 2;
// Declare a vector path.
any vp = path(g.h.[m].n);
$stack.g.h.[$stack.m].n
// Apply it
{vp};
33
// Change the variable used for vectoring
m = 1;
// Apply the path again. Now 22 is returned
{vp};
22
// Declare a substitution path. At the moment $stack.x does not exist
any sp = path(g.h.{x}.n);
$stack.g.h.{$stack.x}.n
string x = "a2";
// Now we've got $stack.x apply the path.
{sp};
22
// Change x and apply the path again. Because the substitution was not
// resolved when the path was created the new value of x is honoured
x = "a1";
{sp};
11
// Perform a similar test, this time with the substitution resolved
// when the path is declared.
string y = "a3";
any sp = path(g.h.{y}.n);
$stack.g.h.a3.n
// Apply it
{sp};
33
// Change y and apply the path again. There's no change - we still get 33.
y = "a1";
{sp};
33
// Finally, note that it is an error if a path substution, when applied,
// does not resolve
any sp = path(g.h.{z}.n);
{sp};
file:/C:/.../pathtests.inq
com.inqwell.any.AnyRuntimeException: Substitution did not resolve {$stack.z}
at com.inqwell.any.LocateNode$ResolvePathComponent.visitFunc(LocateNode.java:1091)
at com.inqwell.any.AbstractFunc.accept(AbstractFunc.java:108)
at com.inqwell.any.LocateNode$LocateAction.resolvePathItem(LocateNode.java:563)
at com.inqwell.any.LocateNode$LocateAction.visitMap(LocateNode.java:649)
at com.inqwell.any.AbstractMap.accept(AbstractMap.java:75)
at com.inqwell.any.LocateNode.apply(LocateNode.java:382)
at com.inqwell.any.LocateNode.doLocate(LocateNode.java:366)
at com.inqwell.any.LocateNode.exec(LocateNode.java:199)
at com.inqwell.any.AbstractFunc.execFunc(AbstractFunc.java:86)
at com.inqwell.any.EvalExpr.evalFunc(EvalExpr.java:112)
at com.inqwell.any.EvalExpr.evalFunc(EvalExpr.java:149)
at com.inqwell.any.parser.Inq.main(Inq.java:516)
file:/C:/.../pathtests.inq <parser>(54)
Inq done
The path function is used in the add function, covered in the section on Events, and when specifying GUI rendering information.
