Escher A language for connecting technologies using pure metaphors

Syntax and meaning

At heart Escher is a Go package that parses a simple written syntax into a labeled graph data structure, called a circuit. If you view XML as a syntax that represents labeled trees, then Escher would be a syntax that represents labeled graphs.

Syntax comparison with Java and C++

As Escher is a so called Conceptual Programming Language, it uses concepts that are very different then what you may know from Object Oriented or Functional Programming, for example. It therefore also uses very different concepts, parts, and names thereof. This section tries to clarify those names.

Declarative unit Java Escher C++
Basic declaration unit class circuit class
Logical group of basic declaration units package faculty namespace
Variable name + type/interface variable name + type gate variable name + type
enclosing declataion unit instance this super gate this
The runtime structure containing all the code class-path index LD_LIBRARY_PATH
Unique string identifier of a declaration unit fully-qualified (class-)name address fully-qualified (class-)name
The underlying runtime JVM Escher runtime the OS
The underlying technology JVM/C/Assembler Golang none/the OS

Circuits

Shows the different parts of a circuit, with each type of a part coded with a diferent color.

A circuit has the strucutre of a graph, consists of nodes, called gates, and edges, called links. Gates have a name and a value:

A circuits links go across pairs of gates. A link has two endpoints, called vectors. Each vector consists of a gate name and a valve name. Vectors do not overlap, in the sense that all vectors with the same gate name (within the circuit) have unique valve names.

Symbolism

Circuits have a standard visual representation that fully captures the internal structure of the circuit, which consists of the gate names and links, and excludes the gate values — the external structure.

To draw a circuit, we start with a solid black oval, denoting the circuit's internal name space. White ovals — contained inside the black one and mutually non-overlapping — denote gates.

Links are depicted as white lines that connect the outlines of gate ovals. Link endpoints connecting to the super gate are attached to the outline of the surrounding black oval.

Valve names are written in white within the black oval, next to their respective visual connection point. Connection points where valve names are visually missing correspond to empty-string valves.

The visual space inside the white gate ovals is reserved for the visual symbolic representation of that value, whatever it might be. If that value is primitive (integer, float, complex, string, directive), we just write it out in black text in the center of the oval. If that value is a circuit, we draw the symbolism for that circuit within the white oval recursively, but this time we switch white and black colors everywhere.

In this illustration, the depicted circuit has three valves at the super gate, labeled as “X”, “Y” and “” (the empty string). The source for this circuit is given later below.

Go interface

Within the Go runtime, circuits are represented by a dedicated type Circuit, whose definition is

type Circuit struct {
	Gate map[Name]Value
	Flow map[Name]map[Name]Vector
}

type Vector struct {
	Gate  Name
	Valve Name
}

type Name interface{}

type Value interface{}

Type Name designates string or int. Type Value designates any Go value.

Using the Escher parser is very simple, in three steps:

The following Go example illustrates this:

package main

import (
	"fmt"
	"github.com/hoijui/escher/pkg/a"
	"github.com/hoijui/escher/pkg/see"
)

func main() {
	src := "alpha { a 123; b 3.14; a: = b:}\n beta { 1, 2, 3, \"abc\" }"
	p := a.NewSrcString(src) // create a parsing object
	for {
		n, v := see.SeePeer(p) // parse one circuit at a time
		if v == nil {
			break
		}
		fmt.Printf("%v %v\n", n, v)
	}
}
Note that parsing errors result in panics.

Grammar

A definition starts with a circuit name followed by a circuit description inside brackets. The name is an alpha-numeric identifier. For instance,

alpha {
	…
}

Between the brackets, one can have any number of statements which are of two kinds: gates and links. Statements are separated by new lines, commas or semi-colons.

Comments

We use a trick: We use syntactic sugared (empty string named), string valued gates, and — purely to visually indicate a comment — we use "//" in the beginning, or "/*" plus "*/" at the end.

alpha {		  `// circuit definition`
	float 1.23 ; `// gate named float with a floating-point value`
	beta {}	; `// gate named beta with an empty circuit value`
	`/*
	  * We can also do this:
	  * A multi-line comment within a circuit definition.
	  * Outside the circuit though, no comments are possible.
	  */`
}

Gates

Gate statements begin on a new line with a gate name identifier, space, and a gate value expression. There are six value types that can be expressed:

Type Represents
Integer native Go type int
Floating-point number native Go type float64
Complex number native Go type complex128
String native Go type string
Directive Escher internal Go type Address, representing a sequence of names, written as dot-separated, fully-qualified names
Circuit Escher internal Go type Circuit

For instance,

alpha {
	directive1 *fully.qualified.Name
	directive2 @fully.qualified.Name
	integral   123
	floating   3.14
	complex    (1-3i)
	quoted     "abcd\n\tefgh"
	backQuoted `
		<html>
			<div>abc</div>
		</html>
	`
}

Gate values can be circuits themselves,

alpha {
	beta {
		Hello World
		Foo   "Bar"
	}
}

Series

Gate names can be omitted in circuit definitions, in which case gates are assigned consecutive integral names, starting from zero. We call the resulting circuits series.

alpha {
	*fully.qualified.Name
	@fully.qualified.Name
	123
	3.14
	(1-3i)
	"abcd\n\tefgh"
	`
		<html>
			<div>abc</div>
		</html>
	`
	{
		A 1
		B "C"
	}
}
which is equivalent to:
alpha {
	0 *fully.qualified.Name
	1 @fully.qualified.Name
	2 123
	3 3.14
	4 (1-3i)
	5 "abcd\n\tefgh"
	6 `
		<html>
			<div>abc</div>
		</html>
	`
	7 {
		A 1
		B "C"
	}
}

Circuit links are semantically symmetric. A link is a pair of two vectors, and a vector consists of a gate name and a valve name.

Vectors are written as the gate name, followed by : (the colon sign), followed by the valve name. Links are written as a vector, followed by optional whitespace, followed by = (the equals sign), followed by another optional whitespace and the second vector. Valid examples:

	and:XAndY = not:X
	and:XAndY= not:X
	and:XAndY =not:X
	and:XAndY=not:X

A few idioms are commonly useful:

Here is a comprehensive example of link definitions:

Nand {
	and *binary.And
	not *binary.Not

	and:X = :X
	and:Y = :Y
	and:XAndY = not:Z
	not:NotZ = :
}

Syntactic sugar

When circuits are used to represent programs — in other words, executable code — it is common to include a gate and then link to its default valve. To reduce verbosity in this case, link definitions support a piece of syntactic sugar.

Either (or both) vectors in a link definition can be substituted for a gate value. This will be expanded into a gate definition with an automatically-generated name and a link to its default gate in sugar-free syntax. For example,

	sum:X = 123
Will be expanded into
	0 123
	sum:Summand = 0:

In this example, both sides of the equation are sugared:

	*os.Scanln = *os.Println
This will expand to:
	0 *os.Scanln
	1 *os.Println
	0: = 1: