This file has been translated from LaTeX by HeVeA.

Node: Section 3-5,	Next: Subsection 3-5-1,	Prev: Section 3-4,	Up: Chapter 3
  

3.5   Collapsing the hierarchy, .SUBDIRS bodies
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

   
  Some projects have many subdirectories that all have the same
configuration. For instance, suppose you have a project with many
subdirectories, each containing a set of images that are to be composed
into a web page. Apart from the specific images, the configuration of
each file is the same.
  To make this more concrete, suppose the project has four
subdirectories 'page1', 'page2', 'page3', and 'page4'. Each contains two
files 'image1.jpg' and 'image2.jpg' that are part of a web page
generated by a program 'genhtml'.
  Instead of of defining a 'OMakefile' in each directory, we can define
it as a body to the '.SUBDIRS' command.
<<    .SUBDIRS: page1 page2 page3 page4
          index.html: image1.jpg image2jpg
              genhtml $+ > $@
>>
  
  The body of the '.SUBDIRS' is interpreted exactly as if it were the
'OMakefile', and it can contain any of the normal statements. The body
is evaluated in the subdirectory for each of the subdirectories. We can
see this if we add a statement that prints the current directory
('$(CWD)').
<<    .SUBDIRS: page1 page2 page3 page4
          println($(absname $(CWD)))
          index.html: image1.jpg image2jpg
              genhtml $+ > $@
    # prints
      /home/jyh/.../page1
      /home/jyh/.../page2
      /home/jyh/.../page3
      /home/jyh/.../page4
>>
  
* Menu:

* Subsection 3-5-1::	Using glob patterns
* Subsection 3-5-2::	Simplified sub-configurations
* Subsection 3-5-3::	Computing the subdirectory list
* Subsection 3-5-4::	Temporary directories


Node: Subsection 3-5-1,	Next: Subsection 3-5-2,	Prev: Section 3-5,	Up: Section 3-5
  

3.5.1   Using glob patterns
===========================
  
  Of course, this specification is quite rigid. In practice, it is
likely that each subdirectory will have a different set of images, and
all should be included in the web page. One of the easier solutions is
to use one of the directory-listing functions, like 'glob' or 'ls'. The
'glob' function takes a shell pattern, and returns an array of file with
matching filenames in the current directory.
<<    .SUBDIRS: page1 page2 page3 page4
          IMAGES = $(glob *.jpg)
          index.html: $(IMAGES)
              genhtml $+ > $@
>>
  

Node: Subsection 3-5-2,	Next: Subsection 3-5-3,	Prev: Subsection 3-5-1,	Up: Section 3-5
  

3.5.2   Simplified sub-configurations
=====================================
  
  Another option is to add a configuration file in each of the
subdirectories that defines directory-specific information. For this
example, we might define a file 'BuildInfo.om' in each of the
subdirectories that defines a list of images in that directory. The
'.SUBDIRS' line is similar, but we include the BuildInfo file.
<<    .SUBDIRS: page1 page2 page3 page4
          include BuildInfo   # Defines the IMAGES variable
  
          index.html: $(IMAGES)
              genhtml $+ > $@
>>
  
  Where we might have the following configurations.
<<   page1/BuildInfo.om:
         IMAGES[] = image.jpg
     page2/BuildInfo.om:
         IMAGES[] = ../common/header.jpg winlogo.jpg
     page3/BuildInfo.om:
         IMAGES[] = ../common/header.jpg unixlogo.jpg daemon.jpg
     page4/BuildInfo.om:
         IMAGES[] = fee.jpg fi.jpg foo.jpg fum.jpg
>>
  

Node: Subsection 3-5-3,	Next: Subsection 3-5-4,	Prev: Subsection 3-5-2,	Up: Section 3-5
  

3.5.3   Computing the subdirectory list
=======================================
  
  The other hardcoded specification is the list of subdirectories
'page1', ..., 'page4'. Rather than editing the project 'OMakefile' each
time a directory is added, we could compute it (again with 'glob').
<<    .SUBDIRS: $(glob page*)
          index.html: $(glob *.jpg)
              genhtml $+ > $@
>>
  
  Alternately, the directory structure may be hierarchical. Instead of
using 'glob', we could use the 'subdirs' function, returns each of the
directories in a hierarchy. For example, this is the result of
evaluating the 'subdirs' function in the omake project root. The 'P'
option, passed as the first argument, specifies that the listing is
"proper," it should not include the 'omake' directory itself.
<<    osh> subdirs(P, .)
      - : <array
              /home/jyh/.../omake/mk : Dir
              /home/jyh/.../omake/RPM : Dir
              ...
              /home/jyh/.../omake/osx_resources : Dir>
>>
  
  Using 'subdirs', our example is now as follows.
<<    .SUBDIRS: $(subdirs P, .)
          index.html: $(glob *.jpg)
              genhtml $+ > $@
>>
  
  In this case, every subdirectory will be included in the project.
  If we are using the 'BuildInfo.om' option. Instead of including every
subdirectory, we could include only those that contain a 'BuildInfo.om'
file. For this purpose, we can use the 'find' function, which traverses
the directory hierarchy looking for files that match a test expression.
In our case, we want to search for files with the name 'BuildInfo.om'.
Here is an example call.
<<    osh> FILES = $(find . -name BuildInfo.om)
      - : <array
              /home/jyh/.../omake/doc/html/BuildInfo.om : File
              /home/jyh/.../omake/src/BuildInfo.om : File
              /home/jyh/.../omake/tests/simple/BuildInfo.om : File>
      osh> DIRS = $(dirof $(FILES))
      - : <array
              /home/jyh/.../omake/doc/html : Dir
              /home/jyh/.../omake/src : Dir
              /home/jyh/.../omake/tests/simple : Dir>
>>
  
  In this example, there are three 'BuildInfo.om' files, in the
'doc/html', 'src', and 'tests/simple' directories. The 'dirof' function
returns the directories for each of the files.
  Returning to our original example, we modify it as follows.
<<    .SUBDIRS: $(dirof $(find . -name BuildInfo.om))
          include BuildInfo   # Defines the IMAGES variable
  
          index.html: $(IMAGES)
              genhtml $+ > $@
>>
  

Node: Subsection 3-5-4,	Next: Chapter 4,	Prev: Subsection 3-5-3,	Up: Section 3-5
  

3.5.4   Temporary directories
=============================
  
  Sometimes, your project may include temporary directories--directories
where you place intermediate results. these directories are deleted
whenever the project is cleanup up. This means, in particular, that you
can't place an 'OMakefile' in a temporary directory, because it will be
removed when the directory is removed.
  Instead, if you need to define a configuration for any of these
directories, you will need to define it using a '.SUBDIRS' body.
<<    section
          CREATE_SUBDIRS = true
  
          .SUBDIRS: tmp
              # Compute an MD5 digest
              %.digest: %.comments
                 echo $(digest $<) > $@
  
              # Extract comments from the source files
              %.comments: ../src/%.src
                 grep '^#' $< > $@
  
              .DEFAULT: foo.digest
  
      .PHONY: clean
  
      clean:
          rm -rf tmp        
>>
  
  In this example, we define the 'CREATE_SUBDIRS' variable as true, so
that the 'tmp' directory will be created if it does not exist. The
'.SUBDIRS' body in this example is a bit contrived, but it illustrates
the kind of specification you might expect. The 'clean' phony-target
indicates that the 'tmp' directory should be removed when the project is
cleaned up.
   

Node: Chapter 4,	Next: Section 4-1,	Prev: Section 3-5,	Up: Top
  

Chapter 4     OMake concepts and syntax
***************************************
    
  Projects are specified to omake with OMakefiles. The OMakefile has a
format similar to a Makefile. An OMakefile has three main kinds of
syntactic objects: variable definitions, function definitions, and rule
definitions.
* Menu:

* Section 4-1::	Variables
* Section 4-2::	Adding to a variable definition
* Section 4-3::	Arrays
* Section 4-4::	Special characters and quoting
* Section 4-5::	Function definitions
* Section 4-6::	Curried functions
* Section 4-7::	Comments
* Section 4-8::	File inclusion
* Section 4-9::	Scoping, sections
* Section 4-10::	Conditionals
* Section 4-11::	Matching
* Section 4-12::	Objects
* Section 4-13::	Classes
* Section 4-14::	Inheritance
* Section 4-15::	static.
* Section 4-16::	Constants


Node: Section 4-1,	Next: Section 4-2,	Prev: Chapter 4,	Up: Chapter 4
  

4.1   Variables
*=*=*=*=*=*=*=*

   
  Variables are defined with the following syntax. The name is any
sequence of alphanumeric characters, underscore '_', and hyphen '-'.
<<   <name> = <value>
>>
  
  Values are defined as a sequence of literal characters and variable
expansions. A variable expansion has the form '$(<name>)', which
represents the value of the '<name>' variable in the current
environment. Some examples are shown below.
<<   CC = gcc
     CFLAGS = -Wall -g
     COMMAND = $(CC) $(CFLAGS) -O2
>>
  
  In this example, the value of the 'COMMAND' variable is the string
'gcc -Wall -g -O2'.
  Unlike make(1), variable expansion is eager and pure (see also the
section on Scoping). That is, variable values are expanded immediately
and new variable definitions do not affect old ones. For example,
suppose we extend the previous example with following variable
definitions.
<<   X = $(COMMAND)
     COMMAND = $(COMMAND) -O3
     Y = $(COMMAND)
>>
  
  In this example, the value of the 'X' variable is the string 'gcc
-Wall -g -O2' as before, and the value of the 'Y' variable is 'gcc -Wall
-g -O2 -O3'.

Node: Section 4-2,	Next: Section 4-3,	Prev: Section 4-1,	Up: Chapter 4
  

4.2   Adding to a variable definition
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

  
  Variables definitions may also use the += operator, which adds the new
text to an existing definition. The following two definitions are
equivalent.
<<   # Add options to the CFLAGS variable
     CFLAGS = $(CFLAGS) -Wall -g
  
     # The following definition is equivalent
     CFLAGS += -Wall -g
>>
  

Node: Section 4-3,	Next: Section 4-4,	Prev: Section 4-2,	Up: Chapter 4
  

4.3   Arrays
*=*=*=*=*=*=

   
  Arrays can be defined by appending the '[]' sequence to the variable
name and defining initial values for the elements as separate lines.
Whitespace on each line is taken literally. The following code sequence
prints 'c d e'.
<<    X[] =
          a b
          c d e
          f
  
      println($(nth 2, $(X)))
>>
  

Node: Section 4-4,	Next: Section 4-5,	Prev: Section 4-3,	Up: Chapter 4
  

4.4   Special characters and quoting
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=

   
  The following characters are special to omake: '$():,=#\'. To treat
any of these characters as normal text, they should be escaped with the
backslash character '\'.
<<    DOLLAR = \$
>>
  
  Newlines may also be escaped with a backslash to concatenate several
lines.
<<    FILES = a.c\
              b.c\
              c.c
>>
  
  Note that the backslash is not an escape for any other character, so
the following works as expected (that is, it preserves the backslashes
in the string).
<<    DOSTARGET = C:\WINDOWS\control.ini
>>
  
  An alternative mechanism for quoting special text is the use '$"..."'
escapes. The number of double-quotations is arbitrary. The outermost
quotations are not included in the text.
<<    A = $""String containing "quoted text" ""
      B = $"""Multi-line
          text.
          The # character is not special"""
>>
  

Node: Section 4-5,	Next: Subsection 4-5-1,	Prev: Section 4-4,	Up: Chapter 4
  

4.5   Function definitions
*=*=*=*=*=*=*=*=*=*=*=*=*=

    
  Functions are defined using the following syntax.
<<   <name>(<params>) =
        <indented-body>
>>
  
  The parameters are a comma-separated list of identifiers, and the body
must be placed on a separate set of lines that are indented from the
function definition itself. For example, the following text defines a
function that concatenates its arguments, separating them with a colon.
<<    ColonFun(a, b) =
          return($(a):$(b))
>>
  
The 'return' expression can be used to return a value
from the function. A 'return' statement is not required; if it is
omitted, the returned value is the value of the last expression in the
body to be evaluated. NOTE: as of version '0.9.6', 'return' is a control
operation, causing the function to immediately return. In the following
example, when the argument 'a' is true, the function 'f' immediately
returns the value 1 without evaluating the print statement.
<<    f(a) =
         if $(a)
            return 1
         println(The argument is false)
         return 0
>>
  
In many cases, you may wish to return a value from a
section or code block without returning from the function. In this case,
you would use the 'value' operator. In fact, the 'value' operator is not
limited to functions, it can be used any place where a value is
required. In the following definition, the variable 'X' is defined as 1
or 2, depending on the value of a, then result is printed, and returned
from the function.
<<    f_value(a) =
         X =
            if $(a)
               value 1
            else
               value 2
         println(The value of X is $(X))
         value $(X)
>>
  
  Functions are called using the GNU-make syntax, '$(<name> <args))',
where '<args>' is a comma-separated list of values. For example, in the
following program, the variable 'X' contains the value 'foo:bar'.
<<   X = $(ColonFun foo, bar)
>>
  
  If the value of a function is not needed, the function may also be
called using standard function call notation. For example, the following
program prints the string "She says: Hello world".
<<    Printer(name) =
          println($(name) says: Hello world)
  
      Printer(She)
>>
  
* Menu:

* Subsection 4-5-1::	Keyword arguments


Node: Subsection 4-5-1,	Next: Section 4-6,	Prev: Section 4-5,	Up: Section 4-5
  

4.5.1   Keyword arguments
=========================
    
  This feature was introduced in version 0.9.8.6.
  Functions can also have keyword parameters and arguments. The syntax
of a keyword parameter/argument is '[~|?]<id> [= <expression>]', where
the keyword name '<id>' is preceeded by the character '~' (for required
arguments), or '?' (for optional arguments). If a default value '=
<expression>' is provided, the argument is always optional.
  Keyword arguments and normal anonymous arguments are completely
separate. Also, it is an error to pass a keyword argument to a function
that does not define it as a keyword parameter.
<<    osh>f(x, ?y = 1, z) =
             add($(mul $x, 100), $(mul $y, 10), $z)
      - : <fun 0>
      osh>f(1, ~y = 2, 3)
      - : 123 : Int
      osh>f(1, 3, ~y = 2)
      - : 123 : Int
      osh>f(1, 3)
      - : 113 : Int
      osh>f(1, 2, 3)
      *** omake error:
         File -: line 11, characters 0-10
         arity mismatch: expected 2 args, got 3
      osh>f(~z = 7)
      *** omake error:
         File -: line 12, characters 0-8
         no such keyword: z
>>
  
  An optional keyword argument defaults to the empty value.
<<    osh> g(?x) =
               println($">>>$x<<<")
      - : <fun 0>
      osh> g()
      >>><<<
      osh> g(~x = xxx)
      >>>xxx<<<
>>
  
  It is an error to omit a required keyword argument.
<<    osh> h(~x, ~y) =
               println(x = $x; y = $y)
      - : <fun 0>
      osh> h(~y = 2, ~x = 1)
      x = 1; y = 2
      osh> h(~y = 2)
      *** omake error:
         File -: line 11, characters 0-9
         keyword argument is required: x
>>
  

Node: Section 4-6,	Next: Section 4-7,	Prev: Section 4-5,	Up: Chapter 4
  

4.6   Curried functions
*=*=*=*=*=*=*=*=*=*=*=*

  
  This feature was introduced in version 0.9.8.6.
  Functions that are marked with the classifier 'curry' can be called
with "too many" arguments. It is expected that a curried function
returns a function that consumes the remaining arguments. All arguments
must be specified.
<<    osh>curry.f(x, y) =
              println($"Got two arguments: x = $x, y = $y")
              g(z) =
                 add($x, $y, $z)
      osh> f(1, 2, 3)
      Got two arguments: x = 1, y = 2
      - : 6 : Int
      osh> f(1, 2)
      Got two arguments: x = 1, y = 2
      *** omake error:
         File -: line 62, characters 0-7
         arity mismatch: expected 1 args, got 0
>>
  
  The function 'apply' can be used to compute partial applications,
whether or not the function is labeled as a curried function.
<<    osh> f1(a, ~b = 2, ~c = 3, d) =
              println($"a = $a, b = $b, c = $c, d = $d")
      - : <fun 0>
      osh> f2 = $(apply $(f1), ~c = 13, 11)
      - : <curry 0>
      osh> f2(14, ~b = 12)
      a = 11, b = 12, c = 13, d = 14
      osh> f2(24)
      a = 11, b = 2, c = 13, d = 24
>>
  

Node: Section 4-7,	Next: Section 4-8,	Prev: Section 4-6,	Up: Chapter 4
  

4.7   Comments
*=*=*=*=*=*=*=

  
  Comments begin with the '#' character and continue to the end of the
line.

Node: Section 4-8,	Next: Section 4-9,	Prev: Section 4-7,	Up: Chapter 4
  

4.8   File inclusion
*=*=*=*=*=*=*=*=*=*=

   

  Files may be included with the 'include' or 'open' form. The included
file must use the same syntax as an OMakefile.
<<    include $(Config_file)
>>
  
  The 'open' operation is similar to an 'include', but the file is
included at most once. 
<<    open Config
  
      # Repeated opens are ignored, so this
      # line has no effect.
      open Config
>>
  
  If the file specified is not an absolute filenmame, both 'include' and
'open' operations search for the file based on the 'OMAKEPATH' variable.
In case of the 'open' directive, the search is performed at parse time,
and the argument to 'open' may not contain any expressions.

Node: Section 4-9,	Next: Section 4-10,	Prev: Section 4-8,	Up: Chapter 4
  

4.9   Scoping, sections
*=*=*=*=*=*=*=*=*=*=*=*

    
  Scopes in omake are defined by indentation level. When indentation is
increased, such as in the body of a function, a new scope is introduced.
  The 'section' form can also be used to define a new scope. For
example, the following code prints the line 'X = 2', followed by the
line 'X = 1'.
<<    X = 1
      section
          X = 2
          println(X = $(X))
  
      println(X = $(X))
>>
  
  This result may seem surprising--the variable definition within the
'section' is not visible outside the scope of the 'section'.
  The 'export' form, which will be described in detail in
Section 6.3*Note Section 6-3::, can be used to circumvent this
restriction by exporting variable values from an inner scope. For
example, if we modify the previous example by adding an 'export'
expression, the new value for the 'X' variable is retained, and the code
prints the line 'X = 2' twice.
<<    X = 1
      section
          X = 2
          println(X = $(X))
          export
  
      println(X = $(X))
>>
  
  There are also cases where separate scoping is quite important. For
example, each OMakefile is evaluated in its own scope. Since each part
of a project may have its own configuration, it is important that
variable definitions in one OMakefile do not affect the definitions in
another.
  To give another example, in some cases it is convenient to specify a
separate set of variables for different build targets. A frequent idiom
in this case is to use the 'section' command to define a separate scope.
<<   section
        CFLAGS += -g
        %.c: %.y
            $(YACC) $<
        .SUBDIRS: foo
  
     .SUBDIRS: bar baz
>>
  
  In this example, the '-g' option is added to the 'CFLAGS' variable by
the 'foo' subdirectory, but not by the 'bar' and 'baz' directories. The
implicit rules are scoped as well and in this example, the newly added
yacc rule will be inherited by the 'foo' subdirectory, but not by the
'bar' and 'baz' ones; furthermore this implicit rule will not be in
scope in the current directory.

Node: Section 4-10,	Next: Section 4-11,	Prev: Section 4-9,	Up: Chapter 4
  

4.10   Conditionals
*=*=*=*=*=*=*=*=*=*

     
  Top level conditionals have the following form.
<<    if <test>
         <true-clause>
      elseif <test2>
         <elseif-clause>
      else
         <else-clause>
>>
  
  The '<test>' expression is evaluated, and if it evaluates to a true
value (see Section 9.2*Note Section 9-2:: for more information on
logical values, and Boolean functions), the code for the '<true-clause>'
is evaluated; otherwise the remaining clauses are evaluated. There may
be multiple 'elseif' clauses; both the 'elseif' and 'else' clauses are
optional. Note that the clauses are indented, so they introduce new
scopes.
  When viewed as a predicate, a value corresponds to the Boolean false,
if its string representation is the empty string, or one of the strings
'false', 'no', 'nil', 'undefined', or '0'. All other values are true.
  The following example illustrates a typical use of a conditional. The
'OSTYPE' variable is the current machine architecture.
<<    # Common suffixes for files
      if $(equal $(OSTYPE), Win32)
         EXT_LIB = .lib
         EXT_OBJ = .obj
         EXT_ASM = .asm
         EXE = .exe
         export
      elseif $(mem $(OSTYPE), Unix Cygwin)
         EXT_LIB = .a
         EXT_OBJ = .o
         EXT_ASM = .s
         EXE =
         export
      else
         # Abort on other architectures
         eprintln(OS type $(OSTYPE) is not recognized)
         exit(1)
>>
  

Node: Section 4-11,	Next: Section 4-12,	Prev: Section 4-10,	Up: Chapter 4
  

4.11   Matching
*=*=*=*=*=*=*=*

    

  Pattern matching is performed with the 'switch' and 'match' forms.
<<    switch <string>
      case <pattern1>
          <clause1>
      case <pattern2>
          <clause2>
      ...
      default
         <default-clause>
>>
  
  The number of cases is arbitrary. The 'default' clause is optional;
however, if it is used it should be the last clause in the pattern
match.
  For 'switch', the string is compared with the patterns literally.
<<    switch $(HOST)
      case mymachine
          println(Building on mymachine)
      default
          println(Building on some other machine)
>>
  
  Patterns need not be constant strings. The following function tests
for a literal match against 'pattern1', and a match against 'pattern2'
with '##' delimiters.
<<   Switch2(s, pattern1, pattern2) =
        switch $(s)
        case $(pattern1)
            println(Pattern1)
        case $"##$(pattern2)##"
            println(Pattern2)
        default
            println(Neither pattern matched)
>>
  
  For 'match' the patterns are egrep(1)-style regular expressions. The
numeric variables '$1, $2, ...' can be used to retrieve values that are
matched by '\(...\)' expressions.
<<    match $(NODENAME)@$(SYSNAME)@$(RELEASE)
      case $"mymachine.*@\(.*\)@\(.*\)"
          println(Compiling on mymachine; sysname $1 and release $2 are
ignored)
  
      case $".*@Linux@.*2\.4\.\(.*\)"
          println(Compiling on a Linux 2.4 system; subrelease is $1)
  
      default
          eprintln(Machine configuration not implemented)
          exit(1)
>>
  

Node: Section 4-12,	Next: Section 4-13,	Prev: Section 4-11,	Up: Chapter 4
  

4.12   Objects
*=*=*=*=*=*=*=

    
  OMake is an object-oriented language. Generally speaking, an object is
a value that contains fields and methods. An object is defined with a
'.' suffix for a variable. For example, the following object might be
used to specify a point (1, 5) on the two-dimensional plane.
<<    Coord. =
          x = 1
          y = 5
          print(message) =
             println($"$(message): the point is ($(x), $(y)")
  
      # Define X to be 5
      X = $(Coord.x)
  
      # This prints the string, "Hi: the point is (1, 5)"
      Coord.print(Hi)
>>
  
  The fields 'x' and 'y' represent the coordinates of the point. The
method 'print' prints out the position of the point.

Node: Section 4-13,	Next: Section 4-14,	Prev: Section 4-12,	Up: Chapter 4
  

4.13   Classes
*=*=*=*=*=*=*=

   
  We can also define classes. For example, suppose we wish to define a
generic 'Point' class with some methods to create, move, and print a
point. A class is really just an object with a name, defined with the
'class' directive.
<<    Point. =
          class Point
  
          # Default values for the fields
          x = 0
          y = 0
  
          # Create a new point from the coordinates
          new(x, y) =
             this.x = $(x)
             this.y = $(y)
             return $(this)
  
          # Move the point to the right
          move-right() =
             x = $(add $(x), 1)
             return $(this)
  
          # Print the point
          print() =
             println($"The point is ($(x), $(y)")
  
      p1 = $(Point.new 1, 5)
      p2 = $(p1.move-right)
  
      # Prints "The point is (1, 5)"
      p1.print()
  
      # Prints "The point is (2, 5)"
      p2.print()
>>
  
  Note that the variable '$(this)' is used to refer to the current
object. Also, classes and objects are functional---the 'new' and
'move-right' methods return new objects. In this example, the object
'p2' is a different object from 'p1', which retains the original (1, 5)
coordinates.

Node: Section 4-14,	Next: Section 4-15,	Prev: Section 4-13,	Up: Chapter 4
  

4.14   Inheritance
*=*=*=*=*=*=*=*=*=

   
  Classes and objects support inheritance (including multiple
inheritance) with the 'extends' directive. The following definition of
'Point3D' defines a point with 'x', 'y', and 'z' fields. The new object
inherits all of the methods and fields of the parent classes/objects.
<<    Z. =
         z = 0
  
      Point3D. =
         extends $(Point)
         extends $(Z)
         class Point3D
  
         print() =
            println($"The 3D point is ($(x), $(y), $(z))")
  
      # The "new" method was not redefined, so this
      # defines a new point (1, 5, 0).
      p = $(Point3D.new 1, 5)
>>
  

Node: Section 4-15,	Next: Subsection 4-15-1,	Prev: Section 4-14,	Up: Chapter 4
  

4.15   static.
*=*=*=*=*=*=*=

    
  The 'static.' object is used to specify values that are persistent
across runs of OMake. They are frequently used for configuring a
project. Configuring a project can be expensive, so the 'static.' object
ensure that the configuration is performed just once. In the following
(somewhat trivial) example, a 'static' section is used to determine if
the LaTeX command is available. The '$(where latex)' function returns
the full pathname for 'latex', or 'false' if the command is not found.
<<   static. =
        LATEX_ENABLED = false
        print(--- Determining if LaTeX is installed )
        if $(where latex)
            LATEX_ENABLED = true
            export
  
        if $(LATEX_ENABLED)
           println($'(enabled)')
        else
           println($'(disabled)')
>>
  
  The OMake standard library provides a number of useful functions for
programming the 'static.' tests, as described in
Chapter 14*Note Chapter 14::. Using the standard library, the
above can be rewritten as
<<   open configure/Configure
     static. =
        LATEX_ENABLED = $(CheckProg latex)
>>
  
  As a matter of style, a 'static.' section that is used for
configuration should print what it is doing using the 'ConfMsgChecking'
and 'ConfMsgResult' functions (of course, most of helper functions in
the standard library would do that automatically).
* Menu:

* Subsection 4-15-1::	.STATIC


Node: Subsection 4-15-1,	Next: Section 4-16,	Prev: Section 4-15,	Up: Section 4-15
  

4.15.1   .STATIC
================
     
  This feature was introduced in version 0.9.8.5.
  There is also a rule form of static section. The syntax can be any of
the following three forms.
<<    # Export all variables defined by the body
      .STATIC:
          <body>
  
      # Specify file-dependencies
      .STATIC: <dependencies>
          <body>
  
      # Specify which variables to export, as well as file dependencies
      .STATIC: <vars>: <dependencies>
          <body>
>>
  
  The '<vars>' are the variable names to be defined, the
'<dependencies>' are file dependencies---the rule is re-evaluated if one
of the dependencies is changed. The '<vars>' and '<dependencies>' can be
omitted; if so, all variables defined in the '<body>' are exported.
  For example, the final example of the previous section can also be
implemented as follows.
<<    open configure/Configure
      .STATIC:
          LATEX_ENABLED = $(CheckProg latex)
>>
  The effect is much the same as using 'static.' (instead of '.STATIC').
However, in most cases '.STATIC' is preferred, for two reasons.
  First, a '.STATIC' section is lazy, meaning that it is not evaluated
until one of its variables is resolved. In this example, if
'$(LATEX_ENABLED)' is never evaluated, the section need never be
evaluated either. This is in contrast to the 'static.' section, which
always evaluates its body at least once.
  A second reason is that a '.STATIC' section allows for file
dependencies, which are useful when the '.STATIC' section is used for
memoization. For example, suppose we wish to create a dictionary from a
table that has key-value pairs. By using a '.STATIC' section, we can
perform this computation only when the input file changes (not on every
fun of 'omake'). In the following example the 'awk' function is used to
parse the file 'table-file'. When a line is encountered with the form
key' = 'value, the key/value pair is added the the 'TABLE'.
<<    .STATIC: table-file
          TABLE = $(Map)
          awk(table-file)
          case $'^\([[:alnum:]]+\) *= *\(.*\)'
              TABLE = $(TABLE.add $1, $2)
              export
>>
  
  It is appropriate to think of a '.STATIC' section as a rule that must
be recomputed whenever the dependencies of the rule change. The targets
of the rule are the variables it exports (in this case, the 'TABLE'
variable).
  

4.15.1.1   .MEMO
----------------
     
  A '.MEMO' rule is just like a '.STATIC' rule, except that the results
are not saved between independent runs of 'omake'.
  

4.15.1.2   :key:
----------------
   
  The '.STATIC' and '.MEMO' rules also accept a ':key:' value, which
specifies a "key" associated with the values being computed. It is
useful to think of a '.STATIC' rule as a dictionary that associates keys
with their values. When a '.STATIC' rule is evaluated, the result is
saved in the table with the ':key:' defined by the rule (if a ':key:' is
not specified, a default key is used instead). In other words, a rule is
like a function. The ':key:' specifies the function "argument", and the
rule body computes the result.
  To illustrate, let's use a '.MEMO' rule to implement a Fibonacci
function.
<<    fib(i) =
          i = $(int $i)
          .MEMO: :key: $i
              println($"Computing fib($i)...")
              result =
                  if $(or $(eq $i, 0), $(eq $i, 1))
                      value $i
                  else
                      add($(fib $(sub $i, 1)), $(fib $(sub $i, 2)))
          value $(result)
  
      println($"fib(10) = $(fib 10)")
      println($"fib(12) = $(fib 12)")
>>
  When this script is run, it produces the following output.
<<    Computing fib(10)...
      Computing fib(9)...
      Computing fib(8)...
      Computing fib(7)...
      Computing fib(6)...
      Computing fib(5)...
      Computing fib(4)...
      Computing fib(3)...
      Computing fib(2)...
      Computing fib(1)...
      Computing fib(0)...
      fib(10) = 55
      Computing fib(12)...
      Computing fib(11)...
      fib(12) = 144
>>
  Note that the Fibonacci computation is performed just once for each
value of the argument, rather than an exponential number of times. In
other words, the '.MEMO' rule has performed a memoization, hence the
name. Note that if '.STATIC' were used instead, the values would be
saved across runs of 'omake'.
  As a general guideline, whenever you use a '.STATIC' or '.MEMO' rule
within a function body, you will usually want to use a ':key:' value to
index the rule by the function argument. However, this is not required.
In the following, the '.STATIC' rule is used to perform some expensive
computation once.
<<    f(x) =
          .STATIC:
              y = $(expensive-computation)
          add($x, $y)
>>
  
  Additonal care should be taken for recursive functions, like the
Fibonacci function. If the ':key:' is omitted, then the rule would be
defined in terms of itself, resulting in a cyclic dependency. Here is
the output of the Fibonacci program with an omitted ':key:'.
<<    Computing fib(10)...
      Computing fib(8)...
      Computing fib(6)...
      Computing fib(4)...
      Computing fib(2)...
      Computing fib(0)...
      fib(10) = 0
      fib(12) = 0
>>
  The reason for this behavior is that the 'result' value is not saved
until the base case 'i = 0 || i = 1' is reached, so 'fib' calls itself
recursively until reaching 'fib(0)', whereupon the 'result' value is
fixed at 0.
  In any case, recursive definitions are perfectly acceptable, but you
will usually want a ':key:' argument so that each recursive call has a
different ':key:'. In most cases, this means that the ':key:' should
include all arguments to the function.

Node: Section 4-16,	Next: Chapter 5,	Prev: Section 4-15,	Up: Chapter 4
  

4.16   Constants
*=*=*=*=*=*=*=*=

   
  Internally, OMake represents values in several forms, which we list
here.
  
  
   - int
 
     
      - Constructor: '$(int <i>)' 9.4.1*Note Subsection 9-4-1::. 
      - Object: 'Int' 12.1.4*Note Subsection 12-1-4::. 
      - An integer is a represented with finite precision using the
      OCaml representation (31 bits on a 32 platform, and 63 bits on a
      64 bit platform). 
      - See also: arithmetic 9.4.3*Note Subsection 9-4-3::. 
 
 
   - float
 
     
      - Constructor: '$(float <x>)' 9.4.2*Note Subsection 9-4-2::. 
      - Object: 'Float' 12.1.5*Note Subsection 12-1-5::. 
      - A float is a floating-point value, represented in IEEE 64-bit
      format. 
      - See also: arithmetic 9.4.3*Note Subsection 9-4-3::. 
 
 
   - array
 
     
      - Constructor: '$(array <v1>, ...,
      <vn>)' 9.3.1*Note Subsection 9-3-1::. 
      - Object: 'Array' 12.1.7*Note Subsection 12-1-7::. 
      - An array is a finite list of values. Arrays are also defined
      with an array definition 
      <<    X[] =
                <v1>
                ...
                <vn>
      >>
    
      - See also: 'nth' 9.3.5*Note Subsection 9-3-5::,
      'nth-tl' 9.3.8*Note Subsection 9-3-8::,
      'length' 9.3.4*Note Subsection 9-3-4::, ... 
 
 
   - string
 
     
      - Object: 'String' 12.1.8*Note Subsection 12-1-8::. 
      - By default, all constant character sequences represent strings,
      so the simple way to construct a string is to write it down.
      Internally, the string may be parsed as several pieces. A string
      often represents an array of values separated by whitespace. 
      <<    osh>S = This is a string
            - : <sequence
               "This" : Sequence
               ' ' : White
               "is" : Sequence
               ' ' : White
               "a" : Sequence
               ' ' : White
               "string" : Sequence>
               : Sequence
            osh>length($S)
            - : 4 : Int
      >>
    
    
      - A data string is a string where whitespace is taken literally.
      It represents a single value, not an array. The constructors are
      the quotations '$"..."' and '$'...''.
      <<    osh>S = $'''This is a string'''
            - : <data "This is a string"> : String
      >>
    
    
      - See also: Quoted strings 7.2*Note Section 7-2::.
      
 
 
   - file
 
     
      - Constructor: '$(file <names>)' 10.1.1*Note Subsection 10-1-1::.
      
      - Object: 'File' 12.1.13*Note Subsection 12-1-13::. 
      - A file object represents the abstract name for a file. The file
      object can be viewed as an absolute name; the string
      representation depends on the current directory.
      <<    osh>name = $(file foo)
            - : /Users/jyh/projects/omake/0.9.8.x/foo : File
            osh>echo $(name)
            foo
            osh>cd ..
            - : /Users/jyh/projects/omake : Dir
            osh>echo $(name)
            0.9.8.x/foo
      >>
    
    
      - See also: 'vmount' 10.6.1*Note Subsection 10-6-1::. 
 
 
   - directory
 
     
      - Constructor: '$(dir <names>)' 10.1.1*Note Subsection 10-1-1::. 
      - Object: 'Dir' 12.1.14*Note Subsection 12-1-14::. 
      - A directory object is like a file object, but it represents a
      directory. 
 
 
   - map (dictionary) 
     
      - Object: 'Map' 12.1.2*Note Subsection 12-1-2::. 
      - A map/dictionary is a table that maps values to values. The
      'Map' object is the empty map. The data structure is persistent,
      and all operations are pure and functional. The special syntax
      '$|key|' can be used for keys that are strings.
      <<    osh>table = $(Map)
            osh>table = $(table.add x, int)
            osh>table. +=
                    $|y| = int
            osh>table.find(y)
            - : "int" : Sequence
      >>
 
 
   - channel
 
     
      - Constructor: '$(fopen <filename>,
      <mode>)' 10.8.4*Note Subsection 10-8-4::. 
      - Objects: 'InChannel' 12.1.16*Note Subsection 12-1-16::,
      'OutChannel' 12.1.17*Note Subsection 12-1-17::. 
      - Channels are used for buffered input/output. 
 
 
   - function
 
     
      - Constructor: '$(fun <params> =>
      <body>)' 9.5.1*Note Subsection 9-5-1::. 
      - Object: 'Fun' 12.1.9*Note Subsection 12-1-9::. 
      - Functions can be defined in several ways. 
        
         - As an anonymous function, 
         <<    $(fun i, j => $(add $i, $j))
         >>
       
         - As a named function, 
         <<    f(i, j) =
                   add($i, $j)
         >>
       
         - (This feature was introduced in version 0.9.8.6.) As an
         anonymous function argument. 
         <<    osh>foreach(i => $(add $i, 1), 1 2 3)
               - : <array 2 3 4> : Array
         >>
     
 
 
   - lexer
 
     
      - Object: 'Lexer' 10.11.9*Note Subsection 10-11-9::. 
      - This object represents a lexer. 
 
 
   - parser
 
     
      - Object: 'Parser' 10.11.13*Note Subsection 10-11-13::. 
      - This object represents a parser. 
  
  
   

Node: Chapter 5,	Next: Section 5-1,	Prev: Chapter 4,	Up: Top
  

Chapter 5     Variables and Naming
**********************************
    
  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 namespace. The 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 namespace
      private.X = 1
      # current object
      this.X = 2
      # public, globally defined
      global.X = 3
>>
  
* Menu:

* Section 5-1::	private.
* Section 5-2::	this.
* Section 5-3::	global.
* Section 5-4::	protected.
* Section 5-5::	public.
* Section 5-6::	Qualified blocks
* Section 5-7::	declare


Node: Section 5-1,	Next: Section 5-2,	Prev: Chapter 5,	Up: Chapter 5
  

5.1   private.
*=*=*=*=*=*=*=

   
  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.
  
  
   1. Private variables are local to the file in which they are defined.
   
   2. Private variables are not exported by the 'export' directive,
   unless they are mentioned explicitly in the 'export' directive.
   <<       private. =
               FLAG = true
     
            section
               FLAG = false
               export
     
            # FLAG is still true
            section
               FLAG = false
               export FLAG
     
            # FLAG is now false
       >>
  

Node: Section 5-2,	Next: Section 5-3,	Prev: Section 5-1,	Up: Chapter 5
  

5.2   this.
*=*=*=*=*=*

   
  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)
>>
  
  In general, it is a good idea to define object variables as protected.
The resulting code is more modular because variables in your object will
not produce unexpected clashes with variables defined in other parts of
the project.

Node: Section 5-3,	Next: Section 5-4,	Prev: Section 5-2,	Up: Chapter 5
  

5.3   global.
*=*=*=*=*=*=*

   
  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 protected 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()
>>
  

Node: Section 5-4,	Next: Section 5-5,	Prev: Section 5-3,	Up: Chapter 5
  

5.4   protected.
*=*=*=*=*=*=*=*=

   
  In OMake 0.9.8, 'protected' is a synonym for 'this'.
<<    osh>protected.x = 1
      - : "1" : Sequence
      osh>value $(this.x)
      - : "1" : Sequence
>>
  In 0.9.9, this will change, so that the qualifier 'protected' means
(in 0.9.9) that a variable is local to the current object or file, and
may not be accessed outside it.

Node: Section 5-5,	Next: Section 5-6,	Prev: Section 5-4,	Up: Chapter 5
  

5.5   public.
*=*=*=*=*=*=*

   
  In OMake 0.9.8, 'public' is a synonym for 'global'.
<<    osh>public.x = 1
      - : "1" : Sequence
      osh>value $(global.x)
      - : "1" : Sequence
>>
  In 0.9.9, this will change, so that the qualifier 'public' means (in
0.9.9) that a variable is to be accessible from outside the current file
or object.

Node: Section 5-6,	Next: Section 5-7,	Prev: Section 5-5,	Up: Chapter 5
  

5.6   Qualified blocks
*=*=*=*=*=*=*=*=*=*=*=

  
  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'.

Node: Section 5-7,	Next: Chapter 6,	Prev: Section 5-6,	Up: Chapter 5
  

5.7   declare
*=*=*=*=*=*=*

   
  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.
  
  
   - If the variable is a function parameter, it is private. 
   - If the variable is defined in an object, it is qualified with
   'this.'. 
   - Otherwise, the variable is public. 
  
   

Node: Chapter 6,	Next: Section 6-1,	Prev: Chapter 5,	Up: Top
  

Chapter 6     Expressions and values
************************************
    
  omake provides a full programming-language including many system and
IO functions. The language is object-oriented -- everything is an
object, including the base values like numbers and strings. However, the
omake language differs from other scripting languages in three main
respects.
  
  
   - Scoping is dynamic. 
   - Apart from IO, the language is entirely functional -- there is no
   assignment operator in the language. 
   - Evaluation is normally eager -- that is, expressions are evaluated
   as soon as they are encountered. 
  
  To illustrate these features, we will use the osh(1) omake program
shell. The osh(1) program provides a toploop, where expressions can be
entered and the result printed. osh(1) normally interprets input as
command text to be executed by the shell, so in many cases we will use
the 'value' form to evaluate an expression directly.
<<    osh> 1
      *** omake error: File -: line 1, characters 0-1 command not found:
1
      osh> value 1
      - : "1" : Sequence
      osh> ls -l omake
      -rwxrwxr-x  1 jyh jyh 1662189 Aug 25 10:24 omake*
>>
  
* Menu:

* Section 6-1::	Dynamic scoping
* Section 6-2::	Functional evaluation
* Section 6-3::	Exporting the environment
* Section 6-4::	Objects
* Section 6-5::	Field and method calls
* Section 6-6::	Method override
* Section 6-7::	Super calls


Node: Section 6-1,	Next: Section 6-2,	Prev: Chapter 6,	Up: Chapter 6
  

6.1   Dynamic scoping
*=*=*=*=*=*=*=*=*=*=*

  
  Dynamic scoping means that the value of a variable is determined by
the most recent binding of the variable in scope at runtime. Consider
the following program.
<<    OPTIONS = a b c
      f() =
         println(OPTIONS = $(OPTIONS))
      g() =
         OPTIONS = d e f
         f()
>>
  
  If 'f()' is called without redefining the 'OPTIONS' variable, the
function should print the string 'OPTIONS = a b c'.
  In contrast, the function 'g()' redefines the 'OPTIONS' variable and
evaluates 'f()' in that scope, which now prints the string 'OPTIONS = d
e f'.
  The body of 'g' defines a local scope -- the redefinition of the
'OPTIONS' variable is local to 'g' and does not persist after the
function terminates.
<<    osh> g()
      OPTIONS = d e f
      osh> f()
      OPTIONS = a b c
>>
  
  Dynamic scoping can be tremendously helpful for simplifying the code
in a project. For example, the OMakeroot file defines a set of functions
and rules for building projects using such variables as 'CC', 'CFLAGS',
etc. However, different parts of a project may need different values for
these variables. For example, we may have a subdirectory called 'opt'
where we want to use the '-03' option, and a subdirectory called 'debug'
where we want to use the '-g' option. Dynamic scoping allows us to
redefine these variables in the parts of the project without having to
redefine the functions that use them.
<<    section
         CFLAGS = -O3
         .SUBDIRS: opt
      section
         CFLAGS = -g
         .SUBDIRS: debug
>>
  
  However, dynamic scoping also has drawbacks. First, it can become
confusing: you might have a variable that is intended to be private, but
it is accidentally redefined elsewhere. For example, you might have the
following code to construct search paths.
<<   PATHSEP = :
     make-path(dirs) =
        return $(concat $(PATHSEP), $(dirs))
  
     make-path(/bin /usr/bin /usr/X11R6/bin)
     - : "/bin:/usr/bin:/usr/X11R6/bin" : String
>>
  
  However, elsewhere in the project, the 'PATHSEP' variable is redefined
as a directory separator '/', and your function suddenly returns the
string '/bin//usr/bin//usr/X11R6/bin', obviously not what you want.
  The 'private' block is used to solve this problem. Variables that are
defined in a 'private' block use static scoping -- that is, the value of
the variable is determined by the most recent definition in scope in the
source text.
<<   private
        PATHSEP = :
     make-path(dirs) =
        return $(concat $(PATHSEP), $(dirs))
  
     PATHSEP = /
     make-path(/bin /usr/bin /usr/X11R6/bin)
     - : "/bin:/usr/bin:/usr/X11R6/bin" : String
>>
  
