Escher A language for connecting technologies using pure metaphors

POSIX faculty

The os faculty contains various reflexes for interacting with the POSIX environment within which an Escher program executes. It contains a few simple reflexes for accessing things like command-line arguments, environment variables, standard file descriptors, process execution and the like.

Most reflexes in os are implemented in less than 10 lines of code and in that sense their implementation is their best documentation. Here we detail only the process execution reflex os.Process.

Process execution reflex

The process reflex requires that three valves, named :Command, :IO and :Exit, be connected. When a command description is sent to the :Command valve, the reflex spawns an OS process, described by the command value. Subsequent commands will block until the spawned process exits. As soon as the process is executed, a circuit value is sent out to the valve :IO, containing the standard file descriptors of the executing process. When the process exits, a circuit value, containing the exit code of the process, is sent out to the :Exit valve.

An example of the command circuit value is as follows:

{
	Env {
		"PATH=/abc:/bin"
		"LESS=less"
	}
	Dir "/home/petar"
	Path "/bin/ls"
	Args { "-l", "/" }
}

The returned IO circuit value is of the following form:

{
	Stdin (io.WriteCloser)
	Stdout (io.ReadCloser)
	Stderr (io.ReadCloser)
}

Where the gate values' types, in brackets, are the Go types io.WriteCloser, io.ReadCloser and io.ReadCloser, respectively.

The exit circuit is of the form

{
	Exit (int)
}

The following example demonstrates invoking the /bin/ls command and forwarding its standard output and error to those of the Escher program itself.

{
	proc *os.Process
	proc:Command = {
		Path "/bin/ls"
		Args { "/" }
	}

	yio *Fork
	proc:IO = yio:

	yio:Stdin = *Ignore
	yio:Stdout = *os.Stdout
	yio:Stderr = *os.Stderr

	yexit *Fork
	proc:Exit = yexit:
	
	exit *os.Exit
	yexit:Exit = exit:
}

The standard file descriptors of the child process must always be handled. In this example, standard output and error are forwarded while standard input is “ignored”. The reflex *Ignore is a “smart” reflex which ignores primitive values (integers, floats, etc.), whereas it closes io.Closer objects and it drains io.Reader objects.