Jump to: | OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long) | |
Index: | All • Variables • Functions • Objects • Targets • Options |
During evaluation, there are three different kinds of namespaces. Variables can be private, or they may refer to fields in the current this object, or they can be part of the global global namespace. In addition, in version 0.9.9 onward, each file has itsown public namespace (see Section 6.10). A variable's namespace can be specified directly by including an explicit qualifier before the variable name. The three namespaces are separate; a variable can be bound in one or more simultaneously.
# private, local to this file osh>private.X = 1 - : "1" : Sequence # A field of the current object osh>this.X = 2 - : "2" : Sequence # Global, dynamically scoped osh>public.X = 3 - : "3" : Sequence osh>value $(this.X) - : "2" : Sequence
The private.
qualifier is used to define variables that are private to the current file/scope.
The values are not accessible outside the scope. Private variables are statically (lexically) scoped.
Obj. = private.X = 1 print() = println(The value of X is: $X) # Prints: # The private value of X is: 1 Obj.print() # This is an error--X is private in Obj y = $(Obj.X)
In addition, private definitions do not affect the global value of a variable.
# The public value of x is 1 x = 1 # This object uses a private value of x Obj. = private.x = 2 print() = x = 3 println(The private value of x is: $x) println(The public value of x is: $(public.x)) f() # Prints: # The private value of x is: 3 # The public value of x is: 1 Obj.print()
Private variables have two additional properties.
export
directive, unless they are
mentioned explicitly by the export
directive.private. = FLAG = true section FLAG = false export # FLAG is still true section FLAG = false export FLAG # FLAG is now false
The this.
qualifier is used to define fields that are local to an object.
Object variables are dynamically scoped.
X = 1 f() = println(The public value of X is: $(X)) # Prints: # The public value of X is: 2 section X = 2 f() # X is a protected field in the object Obj. = this.X = 3 print() = println(The value of this.X is: $(X)) f() # Prints: # The value of this.X is: 3 # The public value of X is: 1 Obj.print() # This is legal, it defines Y as 3 Y = $(Obj.X)
The global.
qualifier is used to specify global dynamically-scoped variables. In the following
example, the global.
definition specifies that the binding X = 4
is to be dynamically
scoped. Global variables are not defined as fields of an object.
X = 1 f() = println(The global value of X is: $(X)) # Prints: # The global value of X is: 2 section X = 2 f() Obj. = this.X = 3 print() = println(The "this" value of X is: $(X)) global.X = 4 f() # Prints: # The protected value of X is: 3 # The global value of X is: 4 Obj.print()
This feature will be introduced in version 0.9.9.0.
The qualifier protected
means that a variable is local to the current object or file, and may
not be accessed outside it.
This feature will be introduced in version 0.9.8.5.
The qualifier public
means that a variable is publically accessible, and may be accessed
outside the current file or object.
This feature will be introduced in version 0.9.9.0.
The qualifier const
is used for variables that are to be defined exactly once.
Any additional definitions are an error.
osh>const.one = 1 - : "1" : Sequence osh>one = 2 *** omake error: File -: line 2, characters 0-7 Modifying a const variable: protected.const.[interactive shell input]::one The variable was defined at the following location File -: line 1, characters 0-13
This feature will be introduced in version 0.9.9.0.
The qualifier auto
is used for variables that are to be auto-exported from all blocks in
scope. The export
does not have to be explicit.
osh>auto.i = 0 osh>foreach(j => ..., 1 2 3 4 5) auto.i = $(add $i, $j) osh>value $i - : 30 : Int
It is important to keep in mind that the auto-export status of a variable does not escape its scope. For example, we might think of writing a “reference-cell” kind of object.
osh>Ref. = auto.contents = this.new(x) = auto.contents = $x value $(this) this.set(x) = auto.contents = $x this.get() = value $(contents) osh>cell = $(Ref.new 1) osh>cell.get() *** omake error: File /Users/jyh/projects/omake/git/auto/x.om: line 9, characters 14-25 unbound variable: auto.[x.om]::contents
The reason for the error is that the variable auto.contents
is exported only within its scope.
The definition cell = $(Ref.new 1)
is not in its scope, so the value is not exported.
If several qualified variables are defined simultaneously, a block form of qualifier can be defined.
The syntax is similar to an object definition, where the name of the object is the qualifier itself.
For example, the following program defines two private variables X
and Y
.
private. = X = 1 Y = 2
The qualifier specifies a default namespace for new definitions in the block. The contents of the block is otherwise general.
private. = X = 1 Y = 2 public.Z = $(add $X, $Y) # Prints "The value of Z is 3" echo The value of Z is $Z
Stylistically, it is usually better to avoid large qualified blocks because the qualifier status can be easy to forget. For example, consider the following fragment.
private. = # Large code sequence ... # build foo.o with -g option (ERROR) CFLAGS = -g foo.o:
In this case, the programmer probably forgot that the definition of the variable CFLAGS
is in
the private
block, so a fresh variable private.CFLAGS
is being defined, not the global
one. The target foo.o
does not use this definition of CFLAGS
.
When a variable name is unqualified, its namespace is determined by the most recent definition or
declaration that is in scope for that variable. We have already seen this in the examples, where a
variable definition is qualified, but the subsequent uses are not qualified explicitly. In the
following example, the first occurrence of $X
refers to the private definition,
because that is the most recent. The public definition of X
is still 0
, but the
variable must be qualified explicitly in order to access the public value.
public.X = 0 private.X = 1 public.print() = println(The value of private.X is: $X) println(The value of public.X is: $(public.X))
Sometimes it can be useful to declare a variable without defining it. For example, we might have a
function that uses a variable X
that is to be defined later in the program. The
declare
directive can be used for this.
declare public.X public.print() = println(The value of X is $X) # Prints "The value of X is 2" X = 2 print()
Finally, what about variables that are used but not explicitly qualified? In this case, the following rules are used.
this.
.
This feature will be introduced in version 0.9.9.0.
In OMake version 0.9.8 and before, there is a single global namespace that all public variables
belong to. This restriction often prevents programs from being scalable. For example, suppose two
developers write their code in a modular fashion, but they happen to use a common variable name
public.X
. There is no harm if the two modules do not call one another, but if they do the
values for X
might conflict.
The most significant change in version 0.9.9 is the introduction of more modular namespaces.
Instead of a single public namespace for an entire project, each file in a project has its own
namespace. There is no single global namespace. The mapping between variable names and their
modules is managed through the use of explicit open
and import
directives.
In practical terms, this usually makes little difference in writing programs. Consider the following program fragment.
# This is file Boo.om open Foo open Bar ... X = 1
The namespace for the variable X
is determined by the most recently opened file that defines
it, or if none do, then the variable is defined in the current file. That is, if the file
Bar
defines X
, then X = 1
is actually Bar::X = 1
; otherwise if
Foo
defines X
, then the definition is Foo::X = 1
; otherwise it is
Boo::X = 1
.
The syntax <File>::<id>
provides an explicit way to specify the namespace. For example, the
following fragment defines Foo::X
even if the file Bar
also defines X
.
open Foo open Bar Foo::X = 1
If a file has multiple components to its path, the module name is dtermined by the final component of the path.
For example, a directive open build/C
defines a module C
.
The open
directive also allows the module name to be specified explicitly, on a subsequent
line with an as
directive. This is also useful if the module name is not statically defined.
open a/Foo as AFoo open b/Foo as BFoo private.myfile = ... open $(myfile) as CFoo AFoo::X = 1
The import
directive is similar to open
, but it does not bind any subsequent names.
Any reference to a variable in a imported file must be fully qualified.
import Foo Foo::X = 1 # Unqualified variables are local to the current file X = 1
The include
directive is similar to textual inclusion.
osh>cat x.om public.Y = $(add $X, 1) osh>X = 1 osh>include x osh>value $Y - : 2 : Int
As we mentioned previously, the namespace for an unqualified variable is determined by the
open
directives in scope. However, for variables that are intended to be part of the current
file's namespace, it is better to qualify the first occurrence explicitly, using either a
declare
directive, or by qualifying the definition with public
or global
.
open Foo open Bar ... public.X = 1
The fully-qualified name should be used even if it is known that the files Foo
and Bar
do not define the variable X
. If the files are subsequently modified so that one of them
does define X
, the fully-qualified definition will be unchanged. In contrast, an unqualified
definition would switch from being defined in the current namespace, to a variable defined by the
opened file, which may have unpredictable consequences.
The -Wdeclare
option can be used to help enforce this style restriction. When the
-Wdeclare
option is used, a warning is issued whenever the first definition of a
variable is unqualified and the variable is not bound by one of the opened files.
Jump to: | OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long) | |
Index: | All • Variables • Functions • Objects • Targets • Options |