OMake grammar

Jump to: | ‍ ‍ | OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long)
Index: | ‍ ‍ | All • Variables • Functions • Objects • Targets • Options

# Appendix ‍B OMake grammar

- OMake lexical conventions
- The OMake grammar
- Dollar modifiers
- Programming syntax

## B.1 OMake lexical conventions

The OMake language is based on the language for GNU/BSD make, where there are few lexical conventions. Strictly speaking, there are no keywords, and few special symbols.

### B.1.1 Comments

Comments begin with the `#` character and continue to the end-of-line. Text within a comment is unrestricted.

Examples.

       # This is a comment
       # This $comment contains a quote " character

### B.1.2 Special characters

The following characters are special in some contexts.

       $    (    )    ,    .   =    :    "    '    `    \    #

- `$` is used to denote a variable reference, or function application.
- Parentheses `)`, `(` are argument deliminters.
- The command `,` is an argument separator.
- The period symbol `.` is a name separator.
- The equality symbol `=` denotes a definition.
- The colon symbol `:` is used to denote rules, and (optionally) to indicate that an expression is followed by an indented body.
- The quotation symbols `"` and `'` delimit character strings.
- The symbol `#` is the first character of a constant.
- The escape symbol `\` is special only when followed by another special character. In this case, the special status of the second character is removed, and the sequence denotes the second character. Otherwise, the `\` is not special.

  Examples:

  - `\$`: the `$` character (as a normal character).
  - `\#`: the `#` character (as a normal character).
  - `\\`: the `\` character (as a normal character).
  - `c\:\Windows\moo\#boo`: the string `c:\Windows\moo#boo`.

### B.1.3 Identifiers

Identifiers (variable names) are drawn from the ASCII alphanumeric characters as well as `_`, `-`, `~`, `@`. Case is significant; the following identifiers are distinct: `FOO`, `Foo`, `foo`. The identifier may begin with any of the valid characters, including digits.

Using `egrep` notation, the regular expression for identifiers is defined as follows.

        identifier ::= [-@~_A-Za-z0-9]+

The following are legal identifiers.

        Xyz    hello_world    seventy@nine
        79-32  Gnus~Gnats     CFLAGS

The following are not legal identifiers.

        x+y    hello&world

### B.1.4 Command identifiers

The following words have special significance when they occur as the first word of a program line. They are not otherwise special.

        case     catch  class    declare    default
        do       else   elseif   export     extends
        finally  if     import   include    match
        open     raise  return   section    switch
        try      value  when     while

### B.1.5 Variable references

A variable reference is denoted with the `$` special character followed by an identifier. If the identifier name has more than one character, it must be enclosed in parentheses. The parenthesized version is most common. The following are legal variable references.

        $(Xyz)    $(hello_world)   $(seventy@nine)
        $(79-32)  $(Gnus~Gnats)    $(CFLAGS)

Single-character references also include several additional identifiers, including `&*<^?][`. The following are legal single-character references.

       $@   $&   $*   $<   $^   $+   $?   $[   $]
       $A   $_   $a   $b   $x   $1   $2   $3

Note that a non-parenthesized variable reference is limited to a single character, even if it is followed by additional legal identifier charqcters. Suppose the value of the `$x` variable is 17. The following examples illustrate evaluation.

        $x           evaluates to    17
        foo$xbar     evaluates to    foo17bar
        foo$(x)bar   evaluates to    foo17bar

The special sequence `$$` represents the character literal `$`. That is, the two-character sequences `\$` and `$$` are normally equalivalent.

### B.1.6 String constants

Literal strings are defined with matching string delimiters. A left string delimiter begins with the dollar-sign `$`, and a non-zero number of single-quote or double-quote characters. The string is terminated with a matching sequence of quotation symbols. The delimiter quotation may not be mixed; it must contain only single-quote characters, or double-quote characters. The following are legal strings.

        $'Hello world'
        $"""printf("Hello world\n")"""
        $''''
    Large "block" of
    text # spanning ''multiple'' lines''''

The string delimiters are not included in the string constant. In the single-quote form, the contents of the string are interpreted verbatim–there are no special characters.

The double-quote form permits expression evaluation within the string, denoted with the `$` symbol. The following are some examples.

        X = Hello
        Y = $""$X world""             # Hello world
        Z = $'''$X world'''           # $X world
        I = 3
        W = $"6 > $(add $I, 2)"       # 6 > 5

Note that quotation symbols without a leading `$` are not treated specially by OMake. The quotation symbols is included in the sequence.

        osh>println('Hello world')
        'Hello world'
        osh>println($'Hello world')
        Hello world
        osh>X = Hello
        - : "Hello" : Sequence
        osh>println('$X world')
        Hello world

## B.2 The OMake grammar

OMake programs are constructed from expressions and statements. Generally, an input program consists of a sequence of statements, each of which consists of one or more lines. Indentation is significant–if a statement consists of more than one line, the second and remaining lines (called the body) are usually indented relative to the first line.

### B.2.1 Expressions

The following table lists the syntax for expressions.

expr | ::= |
|  | (empty)
|  | – Text (see note)
| | | text
| | | string-literal
|  | – Applications
| | | dollar `<char>`
| | | dollar `(` pathid args `)`
|  | – Concatenation
| | | expr expr
dollar | ::= | `$` | `$`` | `$,`
pathid | ::= |
|  | id
| | | pathid `.` id
arg | ::= | expr – excluding special characters `)(,`)
args | ::= | (empty) | arg, ..., arg

An expression is a sequence composed of text, string-literals, variables references and function applications. Text is any sequence of non-special characters.

#### B.2.1.1 Inline applications

An application is the application of a function to zero-or-more arguments. Inline applications begin with one of the “dollar” sequences `$`, `$``, or `$,`. The application itself is specified as a single character (in which case it is a variable reference), or it is a parenthesized list including a function identifier pathid, and zero-or-more comma-separated arguments args. The arguments are themselves a variant of the expressions where the special character `)(,` are not allowed (though any of these may be made non-special with the `\` escape character). The following are some examples of valid expressions.

- `xyz abc`

  The text sequence “`xyz abc`”

- `xyz$wabc`

  A text sequence containing a reference to the variable `w`.

- `$(addsuffix .c, $(FILES))`

  An application of the function `addsuffix`, with first argument `.c`, and second argument `$(FILES)`.

- `$(a.b.c 12)`

  This is a method call. The variable `a` must evaluate to an object with a field `b`, which must be an object with a method `c`. This method is called with argument `12`.

The additional dollar sequences specify evaluation order, `$`` (lazy) and `$,` (eager), as discussed in the section on dollar modifiers (Section ‍B.3).

### B.2.2 Statements and programs

The following table lists the syntax of statements and programs.

params | ::= | (empty) | id, ..., id
target | ::= | expr – excluding special character `:`
program | ::= | stmt `<eol>` ... `<eol>` stmt
stmt | ::= |
|  | – Special forms
| | | command expr optcolon-body
| | | command ( args ) optcolon-body
| | | catch id ( id ) optcolon-body
| | | class id ... id
|  | – Variable definitions
| | | pathid {+}= expr
| | | pathid {+}= `<eol>` indented-body
| | | pathid`[]` {+}= expr
| | | pathid`[]` {+}= `<eol>` indented-exprs
|  | – Functions
| | | pathid(args) optcolon-body
| | | pathid(params) = `<eol>` indented-body
|  | – Objects
| | | pathid `.` {+}= `<eol>` indented-body
|  | – Rules
| | | target : target rule-options `<eol>` indented-body
| | | target :: target rule-options `<eol>` indented-body
| | | target : target : target rule-options `<eol>` indented-body
| | | target :: target : target rule-options `<eol>` indented-body
|  | – Shell commands
| | | expr
indented-body | ::= | (empty)
| | | indented-stmt `<eol>` ... `<eol>` indented-stmt
indented-exprs | ::= | (empty)
| | | indented-expr `<eol>` ... `<eol>` indented-expr
optcolon-body | ::= | (empty)
| | | `<eol>` indented-body
| | | : `<eol>` indented-body
rule-option | ::= | :id: target
rule-options | ::= | (empty)
| | | rule-options rule-option

#### B.2.2.1 Special forms

The special forms include the following.

Conditionals (see the section on conditionals — Section ‍4.10). The `if` command should be followed by an expression that represents the condition, and an indented body. The conditional may be followed by `elseif` and `else` blocks.

        if expr
            indented-body
        elseif expr
            indented-body
        ...
        else
            indented-body

matching (see the section on matching — Section ‍4.11). The `switch` and `match` commands perform pattern-matching. All cases are optional. Each case may include `when` clauses that specify additional matching conditions.

        match(expr)
        case expr
           indented-body
        when expr
           indented-body
        ...
        case expr
           indented-body
        default
           indented-body

Exceptions (see also the `try` function documentation). The `try` command introduces an exception handler. Each `name` is the name of a class. All cases, including `catch`, `default`, and `finally` are optional. The `catch` and `default` clauses contain optional `when` clauses.

        try
            indented-body
        catch name1(id1)
            indented-body
        when expr
            indented-body
        ...
        catch nameN(idN)
            indented-body
        default
            indented-body
        finally
            indented-body

The `raise` command is used to raise an exception.

        raise expr

section (see the `section` description in Section ‍4.9). The `section` command introduces a new scope.

        section
            indented-body

include, open (see also Section ‍4.8). The `include` command performs file inclusion. The expression should evaluate to a file name.

The `open` form is like include, but it performs the inclusion only if the inclusion has not already been performed. The `open` form is usually used to include library files. [jyh– this behavior will change in subsequent revisions.]

        include expr
        open expr

return (see the description of functions in Section ‍4.5). The `return` command terminates execution and returns a value from a function.

        return expr

value (see the description of functions in Section ‍4.5). The `value` command is an identity. Syntactically, it is used to coerce a n expression to a statement.

        value expr

export (see the section on scoping — Section ‍6.3). The `export` command exports a environment from a nested block. If no arguments are given, the entire environment is exported. Otherwise, the export is limited to the specified identifiers.

        export expr

while (see also the `while` function description). The `while` command introduces a `while` loop.

        while expr
            indented-body

class, extends (see the section on objects — Section ‍4.12). The `class` command specifies an identifier for an object. The `extends` command specifies a parent object.

        class id
        extends expr

#### B.2.2.2 Variable definitions

See the section on variables (Section ‍4.1). The simplest variable definition has the following syntax. The `=` form is a new definition. The += form appends the value to an existing definition.

        id = expr
        id += expr

        osh> X = 1
        - : "1" : Sequence
        osh> X += 7
        - : "1" " " "7" : Sequence

A multi-line form is allowed, where the value is computed by an indented body.

        id {+}=
            indented-body

        osh> X =
                 Y = HOME
                 println(Y is $Y)
                 getenv($Y)
        Y is HOME
        - : "/home/jyh" : Sequence

The name may be qualified qith one of the `public`, `prtected`, or `private` modifiers. Public variables are dynamically scoped. Protected variables are fields in the current object. Private variables are statically scoped.

[jyh: revision 0.9.9 introduces modular namespaces; the meaning of these qualifiers is slightly changed.]

        public.X = $(addsuffix .c, 1 2 3)
        protected.Y = $(getenv HOME)
        private.Z = $"Hello world"

#### B.2.2.3 Applications and function definitions

See the section on functions (Section ‍4.5). A function-application statement is specified as a function name, followed a parenthesized list of comma-separated arguments.

        osh> println($"Hello world")

        osh> FILES = 1 2 3
        - : 1 2 3
        osh> addsuffix(.c, $(FILES))
        - : 1.c 2.c 3.c

        # The following forms are equivalent
        osh> value $(println $"Hello world")
        osh> value $(addsuffix .c, $(FILES))
        - : 1.c 2.c 3.c

If the function application has a body, the body is passed (lazily) to the function as its first argument. [jyh: in revision 0.9.8 support is incomplete.] When using `osh`, the application must be followed by a colon `:` to indicate that the application has a body.

        # In its 3-argument form, the foreach function takes
        # a body, a variable, and an array.  The body is evaluated
        # for each element of the array, with the variable bound to
        # the element value.
        #
        # The colon is required only for interactive sessions.
        osh> foreach(x => 1 2 3):
                add($x, 1)
        - : 2 3 4

Functions are defined in a similar form, where the parameter list is specified as a comma-separated list of identifiers, and the body of the function is indented.

        osh> f(i, j) =
                add($i, $j)
        - : <fun 2>
        osh> f(3, 7)
        - : 10 : Int

#### B.2.2.4 Objects

See the section on objects (Section ‍4.12). Objects are defined as an identifier with a terminal period. The body of the object is indented.

        Obj. =
            class Obj

            X = 1
            Y = $(sub $X, 12)
            new(i, j) =
               X = $i
               Y = $j
               value $(this)
            F() =
               add($X, $Y)
            println($Y)

The body of the object has the usual form of an indented body, but new variable definitions are added to the object, not the global environment. The object definition above defines an object with (at least) the fields `X` and `Y`, and methods `new` and `F`. The name of the object is defined with the `class` command as `Obj`.

The `Obj` itself has fields `X = 1` and `Y = -11`. The `new` method has the typical form of a constructor-style method, where the fields of the object are initialized to new values, and the new object returned (`$(this)` refers to the current object).

The `F` method returns the sum of the two fields `X` and `Y`.

When used in an object definition, the += form adds the new definitions to an existing object.

       pair. =
          x = 1
          y = 2

       pair. +=
          y = $(add $y, 3)
       # pair now has fields (x = 1, and y = 5)

The `extends` form specifies inheritance. Multiple inheritance is allowed. At evaluation time, the `extends` directive performs inclusion of the entire parent object.

       pair. =
          x = 1
          y = 2

       depth. =
          z = 3
          zoom(dz) =
             z = $(add $z, $(dz))
             return $(this)

       triple. =
          extends $(pair)
          extends $(depth)

          crazy() =
             zoom($(mul $x, $y))

In this example, the `triple` object has three fields x, y, and z; and two methods `zoom` and `crazy`.

#### B.2.2.5 Rules

See the chapter on rules (Chapter ‍8). A rule has the following parts.

1. A sequence of targets;
2. one or two colons;
3. a sequence of dependencies and rule options;
4. and an indented body.

The targets are the files to be built, and the dependencies are the files it depends on. If two colons are specified, it indicates that there may be multiple rules to build the given targets; otherwise only one rule is allowed.

If the target contains a `%` character, the rule is called implicit, and is considered whenever a file matching that pattern is to be built. For example, the following rule specifies a default rule for compiling OCaml files.

        %.cmo: %.ml %.mli
           $(OCAMLC) -c $<

This rule would be consulted as a default way of building any file with a `.cmo` suffix. The dependencies list is also constructed based on the pattern match. For example, if this rule were used to build a file `foo.cmo`, then the dependency list would be `foo.ml foo.mli`.

There is also a three-part version of a rule, where the rule specification has three parts.

        targets : patterns : dependencies rule-options
           indented-body

In this case, the patterns must contain a single `%` character. Three-part rules are also considered implicit. For example, the following defines a default rule for the `clean` target.

        .PHONY: clean

        clean: %:
            rm -f *$(EXT_OBJ) *$(EXT_LIB)

Three-part implicit rules are inherited by the subdirectories in the exact same way as with the usual two-part implicit rules.

There are several special targets, including the following.

- `.PHONY` : declare a “phony” target. That is, the target does not correspond to a file.
- `.ORDER` : declare a rule for dependency ordering.
- `.INCLUDE` : define a rule to generate a file for textual inclusion.
- `.SUBDIRS` : specify subdirectories that are part of the project.
- `.SCANNER` : define a rule for dependency scanning.

There are several rule options.

- `:optional: dependencies` the subsequent dependencies are optional, it is acceptable if they do not exist.
- `:exists: dependencies` the subsequent dependencies must exist, but changes to not affect whether this rule is considered out-of-date.
- `:effects: targets` the subsequent files are side-effects of the rule. That is, they may be created and/or modified while the rule is executing. Rules with overlapping side-effects are never executed in parallel.
- `:scanner: name` the subsequent name is the name of the `.SCANNER` rule for the target to be built.
- `:value: expr` the `expr` is a “value” dependency. The rule is considered out-of-date whenever the value of the `expr` changes.

Several variables are defined during rule evaluation.

- `$*` : the name of the target with the outermost suffix removed.
- `$>` : the name of the target with all suffixes removed.
- `$@` : the name of the target.
- `$^` : the explicit file dependencies, sorted alphabetically, with duplicates removed.
- `$+` : all explicit file dependencies, with order preserved.
- `$<` : the first explicit file dependency.
- `$&` : the free values of the rule (often used in `:value:` dependencies).

#### B.2.2.6 Shell commands

See the chapter on shell commands (Chapter ‍11).

While it is possible to give a precise specification of shell commands, the informal description is simpler. Any non-empty statement where each prefix is not one of the other statements, is considered to be a shell command. Here are some examples.

        ls                                 -- shell command
        echo Hello world > /dev/null       -- shell command
        echo(Hello world)                  -- function application
        echo(Hello world) > /dev/null      -- syntax error
        echo Hello: world                  -- rule
        X=1 getenv X                       -- variable definition
        env X=1 getenv X                   -- shell command
        if true                            -- special form
        \if true                           -- shell command
        "if" true                          -- shell command

## B.3 Dollar modifiers

Inline applications have a function and zero-or-more arguments. Evaluation is normally strict: when an application is evaluated, the function identifier is evaluated to a function, the arguments are then evaluated and the function is called with the evaluated arguments.

The additional “dollar” sequences specify additional control over evaluation. The token `$`` defines a “lazy” application, where evaluation is delayed until a value is required. The `$,` sequence performs an “eager” application within a lazy context.

To illustrate, consider the expression `$(addsuffix .c, $(FILES))`. The `addsuffix` function appends its first argument to each value in its second argument. The following `osh` interaction demonstrates the normal bahavior.

    osh> FILES[] = a b c
    - : <array a b c>
    osh> X = $(addsuffix .c, $(FILES))
    - : <array ...>
    osh> FILES[] = 1 2 3 # redefine FILES
    - : <array 1 2 3>
    osh> println($"$X")  # force the evaluation and print
    a.c b.c c.c

When the lazy operator `$`` is used instead, evaluation is delayed until it is printed. In the following sample, the value for `X` has changed to the `$(apply ..)` form, but otherwise the result is unchanged because it it printed immediately.

    osh> FILES[] = a b c
    - : <array a b c>
    osh> SUF = .c
    - : ".c"
    osh> X = $`(addsuffix $(SUF), $(FILES))
    - : $(apply global.addsuffix ...)
    osh> println($"$X")  # force the evaluation and print
    a.c b.c c.c

However, consider what happens if we redefine the `FILES` variable after the definition for `X`. In the following sample, the result changes because evaluation occurs after the values for `FILES` has been redefined.

    osh> FILES[] = a b c
    - : <array a b c>
    osh> SUF = .c
    - : ".c"
    osh> X = $`(addsuffix $(SUF), $(FILES))
    - : $(apply global.addsuffix ...)
    osh> SUF = .x
    osh> FILES[] = 1 2 3
    osh> println($"$X")  # force the evaluation and print
    1.x 2.x 3.x

In some cases, more explicit control is desired over evaluation. For example, we may wish to evaluate `SUF` early, but allow for changes to the `FILES` variable. The `$,(SUF)` expression forces early evaluation.

    osh> FILES[] = a b c
    - : <array a b c>
    osh> SUF = .c
    - : ".c"
    osh> X = $`(addsuffix $,(SUF), $(FILES))
    - : $(apply global.addsuffix ...)
    osh> SUF = .x
    osh> FILES[] = 1 2 3
    osh> println($"$X")  # force the evaluation and print
    1.c 2.c 3.c

## B.4 Programming syntax

This feature was introduced in version 0.9.8.6.

The standard OMake ‍language is designed to make it easy to specify strings. By default, all values are strings, and strings are any sequence of text and variable references; quote symbols are not necessary.

        CFLAGS += -g -Wall

The tradeoff is that variable references are a bit longer, requiring the syntax `$(...)`.

The “program syntax” inverts this behavior. The main differences are the following.

- Identifiers represent variables.
- Strings must be quoted.
- Function application is written `f(exp1, ..., expN)`.

It is only the syntax of expressions that changes. The large scale program is as before: a program is a sequence of definitions, commands, indentation is significant, etc. However, the syntax of expressions changes, where an expression is

- the value on the right of a variable definition `Var = <exp>`, or
- an argument to a function.

The following table lists the syntax for expressions.

e | ::= | 0, 1, 2, ... | integers
| | | 0.1, 1E+23, ... | floating-point constants
| | | x, ABC, ... | identifiers
| | | id`::`id | scoped name
| | | id.id. ... id | projection
| | | `-` e | negation
| | | e `+` e | e `-` e | e `*` e | e `/` e | e `%` e | arithmetic
| | | e `^` e | e `&` e | e `|` e | bitwise operations
| | | e `<<` e | e `>>` e | e `>>>` e | shifting
| | | e `&&` e | e `||` e | Boolean operations
| | | e `<` e | e `<=` e | e `=` e | e `>=` e | e `>` e | comparisons
| | | e`(`e`,` ...`,` e`)` | function application
| | | e`[`e`]` | array subscripting
| | | `(` e `)` | parenthesized expressions
| | | `"` ... `"` | `'` ... `'` | strings
| | | `$"` ... `"` | `$'` ... `'` | strings
| | | `$(` ... `)` | variables and applications

Note that the `$`-style expressions are still permitted and even required for

- accessing instance methods of objects as well as
- interpolation of variables or code inside of double-quoted strings.

### B.4.1 Usage

Switch back and forth between conventional OMake syntax and program syntax with

    .LANGUAGE: program

and

    .LANGUAGE: make

where `make` is the default. You can mix normal and program syntax in the same file.

### B.4.2 Examples

First, let us recover some list functions from OCaml.

    .LANGUAGE: program

    ##  Answer whether [xs] is empty.
    is_empty(xs) =
            value length(xs) = 0

    ##  Answer the first element of [xs].
    hd(xs) =
            value nth(0, xs)

    ##  Answer [xs] with the first element removed.
    tl(xs) =
            value nth-tl(1, xs)

    ##  Re-implement the OCaml List function `map':
    ##      val map: ('a -> 'b) -> 'a list -> 'b list
    ##  which applies [f] to all elements of [xs].
    map(f, xs) =
            if is_empty(xs)
                    value xs
            else
                    value array(apply(f, hd(xs)), map(f, tl(xs)))

    ##  Re-implement the OCaml List function `mapi':
    ##      val mapi: (int -> 'a -> 'b) -> 'a list -> 'b list
    ##  which applies [f] to all elements of [xs] and passes the element's
    ##  index along with the element itself.
    mapi(f, xs) =
            iter(n, xs1) =
                    if is_empty(xs1)
                            value xs1
                    else
                            value array(apply(f, n, hd(xs1)), iter(n + int(1), tl(xs1)))
            value iter(int(0), xs)

    ##  Re-implement the OCaml List function `iteri':
    ##      val iteri: (int -> 'a -> unit) -> 'a list -> unit
    ##  Apply [f] to all elements in [xs] and pass the array index as well
    ##  as the array element itself.
    iteri(f, xs) =
            iter(n, xs1) =
                    if is_empty(xs1)
                            return
                    else
                            apply(f, n, hd(xs1))
                            iter(add(n, int(1)), tl(xs1))
            iter(int(0), xs)

    ##  Re-implement the OCaml List function `fold_left':
    ##      val fold_left: ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
    ##  with the usual semantics of
    ##       f (... (f (f a x_1) x_2) ...) x_n.
    fold_left(f, a, xs) =
            if is_empty(xs)
                    value a
            else
                    value fold_left(f, apply(f, a, hd(xs)), tl(xs))

    ##  Select all elements of [xs] that match predicate [p].
    select_if(p, xs) =
            if is_empty(xs)
                    value xs
            else
                    x = hd(xs)
                    rest = tl(xs)
                    if apply(p, x)
                            value array(x, select_if(p, rest))
                    else
                            value select_if(p, rest)

When defining or using objects the program-syntax cannot be used throughout as method calls – and every function application within – must be written in conventional syntax. The following example implements the container data-type of a double-ended queue ("deque") on top OMake arrays.

    .LANGUAGE: program

    Deque. =
            class Deque

            empty() =
                    this.container_[] =
                    value this

            new(a_sequence) =
                    this.container_ = array(a_sequence)
                    value this

            is_empty() =
                    value not($(this.container_.is-nonempty))

            length() =
                    value $(this.container_.length)

            contents() =
                    value this.container_

            ##  Access the first element.
            front() =
                    value nth(0, this.container_)

            ##  Access the last element.
            back() =
                    size = $(this.container_.length)
                    value nth(size - 1, this.container_)

            ##  Append [an_element] to the rear end of the deque.
            push_back(an_element) =
                    this.container_ = array(this.container_, an_element)
                    value this

            ##  Prepend [an_element] to the front end of the deque.
            push_front(an_element) =
                    this.container_ = array(an_element, this.container_)
                    value this

            ##  Remove the last element of the deque.
            pop_back() =
                    size = $(this.container_.length)
                    this.container_ = nth-hd(size - 1, this.container_)
                    value this

            ##  Remove the first element of the deque.
            pop_front() =
                    this.container_ = nth-tl(1, this.container_)
                    value this

            ##  Answer the reversed deque.
            reverse() =
                    this.container_ = this.container_.rev
                    value this

            ##  Answer the result of mapping [a_function] over the whole
            ##  deque (preserving the order of the elements).
            map(a_function) =
                    this.container_ = $(this.container_.map $(a_function))
                    value this

    ##  Simply define SELFTEST and run the file with osh(1):
    ##          env SELFTEST= osh class--deque.om
    if defined-env($'SELFTEST')
            ws = Deque.empty
            printvln(ws)
            println($"empty? ws: $(ws.is_empty)")
            println($"length ws: $(ws.length)")
            println($'----------------------------------------')
            xs = $(Deque.new $(array $(int 10), $(int 11), $(int 12)))
            printvln(xs)
            println($"empty? xs: $(xs.is_empty)")
            println($"length xs: $(xs.length)")
            println($'----------------------------------------')
            xs0 = $(xs.push_front $(int 1))
            xs1 = $(xs0.push_back $(int 99))
            printvln(xs1.back)
            xs2 = xs1.pop_back
            printvln(xs2)
            println($'----------------------------------------')
            printvln(xs2.back)
            xs2_back = xs2.back
            printvln($(xs2_back.instanceof Int))

Jump to: | ‍ ‍ | OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long)
Index: | ‍ ‍ | All • Variables • Functions • Objects • Targets • Options