Language Overview

Files and modules

All the code that Augeas executes must be contained in a module. A file file.aug must start with a line

module File =

Both augparse and augtool search for .aug files in the directories listed in the AUGEAS_LENS_LIB environment variable, followed by the directories given with -I command line arguments, and finally in the default directory /usr/share/augeas/lenses.

Notice that the name of the module must be the capitalized name of the file it is in without the .aug extension.

Lexical conventions

Comments

Comments are anything enclosed in matching pairs of (* and *) delimiters. Comments can be nested.

Variables

Variables are declared inside a module with the keyword let:

let hello = "Hello"

In expressions, variables from the current module and builtins can be referenced without any qualification; variables from other modules must be prefixed with the module name as in Module.var for a variable var declared in the module Module (which must be in a file called module.aug).

Variable names must match the regular expression /[a-z][A-Za-z0-9_]*/.

Strings

Two basic types in the schema language are strings and regular expressions. String literals are enclosed in double quotes, and can contain escape sequences, similar to the escape sequences in C strings. In particular, the sequences \n, and \t are translated to the new line and tab characters respectively. Therefore, the string literal "\t\n" denotes a two character string consisting of a tab and a newline character.

Variables must be declared before they can be used. Variables aren't declared until after the let var = exp statement has been processed; that implies that Augeas does not support recursion, since var can not be used in exp.

Regular expressions

Regular expression literals are enclosed in slashes (/.../). The regular expression syntax is that of extended POSIX regular expressions, with the small difference that . does not match newlines. To put a literal / into a regular expression, escape it as \/. The same escape sequences as for string literals are recognized, so that the regular expresion literal /(\t|\n)/ matches either a tab or a newline character.

Regular expressions can be concatenated with . and unioned with |. They can also be iterated with the usual postfix operators *, +, and ?:

Let forms

Let forms are used to give names to expressions, similar to defining variables in imperative languages; for example

let hello = "Hello"
let greet (name:string) = hello . ", " . name

defines two variables hello and greet. The latter is a function that takes a string as an argument and returns a string resulting from prepending Hello, to that string. Let forms can also be used purely for their sideeffect: if the name after the let is _, Augeas will evaluate the right-hand side, and immediately forget the result. This is useful for printing debug output:

let _ = print_endline (greet "User")

will simply print Hello, User.

Let forms can also be used to define local names within another let:

let greet (name:string) =
  let hello = "Hello" in
  hello . ", " . name

only defines a global name greet. The name hello can only be used within the body of the definition of greet, and will be forgotten afterwards.

Types

Besides the types string and regexp, Augeas has a number of other simple types. All the builtin types in Augeas are:

Type Explanation
unit A type with no values, similar to void in C or Java
string Simple strings, such as "Hello World"
regexp Regular expressions in extended POSIX notation, such as /[a-z]+/
lens Lenses, such as key /[a-z]+/
tree Tree objects, generated by test .. get ..; they are only useful in Unit tests
filter File name filters, generated by incl and excl, such as incl "/etc/yum.repos.d/*"

Functions

Functions are declared similar to variables, by adding a list of parameters and their types to the declaration of the function name:

let x_space_y (x:string) (y:string) = x . " " . y

declares a function of two arguments x and y, which must both be strings. Types are statically checked by the interpreter.

To evaluate a function, simply write

let hello_world = x_space_y "Hello" "World"

You can also create new functions by currying:

let hello_y = x_space_y "Hello"

Now, hello_y is a function that takes one string argument and appends it to the string "Hello" . " ", so that

let hello_world2 = hello_y "World"

has the same value as hello_world.

Augeas comes has a number of builtins, functions that are always defined and that you can use anywhere.

Expressions

Augeas has a few standard operators: binary operators . for concatenation and | for union, and unary postfix operators *, +, and ? for repetition. In addition, the subtree operator [ exp ] builds a subtree lens. All these operators can be applied to lenses; for lenses they are described in detail on the lenses page.

In addition, the concatenation operator can be used to concatenate strings and regular expressions, and the union operator and the repetition operators can be used for regular expressions:

let hello = "Hello"
let world = "World"
let hello_world = hello . " " . world . "\n"

let a = /a/
let b = /b/
let a_b = /a/ . /b/         (* Same as /ab/    *)
let a_or_b = /a/ | /b/      (* Same as /(a|b)/ *)
let a_plus = /a/+           (* Same as /a+/    *)

Strings are automatically coerced to a regular expression matching that string exactly, whenever needed. Characters that have a special meaning inside regular expressions are escaped, so that the string "()" is coerced to the regular expression /\(\)/. The following are legal:

let hello_world_re = "Hello" . /[\t ]/ . "World"
let letter_or_bracks = /[a-z]/ | "[]"

Operators

Here are all the operators supported, with a quick indication of the permissible types for the operands. The operators are listed in order of increasing precedence, i.e. a . b | c is parsed as (a . b) | c. Parentheses can be used to change order of evaluation as in a . (b | c).

All operators take arguments of the same type, and return a new object of that same type. Since strings can automatically be coerced to regular expressions, the expression /a/ . "b" is legal, resulting in the regular expression /ab/. In the following table, a, b, f, g, and l are all variables of a type suitable for the operator:

Operator Meaning Types
f ; g Composition For functions f: t1 -> t2 and g: t2 -> t3 produce h: t1 -> t3 with h x = g (f x). If t2 is unit, g must not take any arguments.
a | b Union regexp, lens
a - b Subtraction regexp
a . b Concatenation string, regexp, lens, filter
a*, a+, a? Repetition regexp, lens
[ l ] Subtree lens

Unit Tests

The Augeas language has a builtin facility for unit testing lenses. Let's first declare a simple lens

let lns = [ key /[a-z]+/ . del /[ \t]*:[ \t]*/ ": " . store /[a-z]+/ . del "\n" "\n"]*

This lens transforms a line like var :  value into a tree { "var" = "value" }. The notation we use for trees is

TREE ::= '{' LABEL '=' VALUE TREE '}' TREE

For example, { "k1" = "v1" } { "k2" = "v2" { "k2.1" } } is a tree with two nodes at the root level, labeled k1 and k2 with values v1 and v2, and k2 has one child labeled k2.1 with no associated value.

get tests

The get direction of lns can be tested with

test lns get "var : value\n" = { "var" = "value" }

The test transforms the string "var : value" using lns and checks that it produces a tree with one node labeled var with value value. The test result after the = can also be a ?, which prints the result of the transformation, and a *, which expects that the test fails with an exception:

test lns get "var : value\n" = ?   (* Prints the tree *)
test lns get "var1 : value\n" = *

The latter test succeeds, because applying the get of lns to var1 : value fails: the regular expression /[a-z]+/ in the key lens for lns does not match var1, so that the get of lns does not process the entire input string, which is flagged as an error.

put tests

Testing the put direction involves a roundtrip from string to tree back to string, with possible modifications of the tree:

test lns put "var : value\n" after set "/var" "othervalue" = "var : othervalue\n"

The test first transforms "var : value" into a tree, then modifies the tree with the commands listed after the after keyword, and transforms the modified tree back to a string. That string is then compared to the right hand side of the test statement. The right handside can either be a string, in which case the test must produce that exact same string, or ? to print the test result, or * to indicate that the transform must fail.

The after commands can make multiple changes to the tree; commands are separated by a ;:

test lns put "var : value" after
   set "/var" "othervalue";
   set "/newvar" "newvalue"
 = "var : othervalue\nnewvar: newvalue"

Besides the command set PATH VALUE, the command rm PATH can be used to remove parts of the tree.