GCI Control Language

Daniel Ramage 28 July 2005

The GCI command grammar describes a language for interacting with applications, files, and processes on a modern Linux desktop. This document describes that language. Where unspecified, things in GCI happen like in Python (e.g. string concatenation with +, equality testing with ==, etc.).

Unbound statements

GCI's primary goal is simple interaction with objects in the system, so we begin by describing the basic type of statement made in GCI. An unbound statement in GCI is a statement that executes in a semantic grammar context determined by the type of the first argument. Types are marked using a prefix: notation. For instance, take the hypothetical command line:

app:xserver set resolution 1280x1024

When evaluating this statement, GCI sees that the first argument's type is marked as 'app'. GCI delegates parsing of this line to the 'app' schema, which uses d-bus to request the set of verbs available for the 'xserver' application. Perhaps {set,get,restart} is returned. When the user types one of these, GCI uses d-bus to request the semantic grammar for that verb. 'xserver' might return a string over d-bus containing the following grammar:

@set := resolution ("1280x1024"|"1024x768"|"800x600") | depth ("24"|"16"|"8")

The returned semantic grammar is used for completion and grammaticality tests on the rest of the line. Thus, GCI knows that 'resolution' and 'depth' can be accepted as the first argument, and which options are valid for each of those fields. When the user presses enter, the command is executed by calling the 'set' method over d-bus.

Executable object types

In the example above, we used the app: prefix to mark the otherwise ambiguous word "xserver" as a d-bus communicable application name. This and two other executable object types are described below:

The architecture for building new invokable object types could easily be extended to support "sql:select" or other types of magic.

Variables and types

Each statement in GCI returns a value with an associated return type. We can assign the result of a statement to a variable with an assignment expression:

res = {app:xserver get resolution} listing = {sh: ls -a}

GCI interprets a curly-braced block as an expression to evaluate. After execution, res will contain the current xserver resolution, and listing will contain an iterable of strings (output of ls split on \n). We can use the values in those variables in later expressions:

app:xserver set resolution {res}

All executable object types described above are valid types of variables. This enables statements like:

browser = app:firefox {browser} show http://www.stanford.edu/

Variables in GCI are dynamically typed. That type might be an executable type (app:, file:, sh:), value type (string, double, int, void), or a collection type (iterable, list, map). Because GCI supports semantic grammars for each expression, these types are strongly enforced when passing arguments to executable object types.

Control structures

The GCI command language supports control structures in a python-like syntax for affecting the context of the GCI parser or modifying control-flow (looping, branching).

Context modifying statements

Control structures

Examples - Putting it all together

Enqueue all mp3's by RJD2.

exec in ~/Music: for filename in {find . -name '*.mp3'}: if {file:{filename} get artist} == 'RJD2' file:{filename} enqueue

Send a message to all contacts.

body = {sh:cat body.txt} subject = "Hi there" tell evolution: for contact in {get contacts}: send message to {contact} with subject {subject} and body {body}