SourceForge.net Logo

One syntax to rule them all

Last modified on 2007/12/09


Syntactic Sugar

Sugared tuples

Harpoon provides a syntactic sugar for tuples, which makes it possible to get rid of brackets in complex descriptions. For example, a tuple describing variable declaration:

var (x, Int)

can be expressed more feasibly:

var x : Int

However, such sugared tuples introduce expressions that are not explicitly ended and thus identifiers can sometimes be mistaken for tags. For example in the following expression it is not clear if "b" is a tag or an identifier:

{ a : b 1 }

The rule for solving such ambiguities is that tags always have higher priority. To force an identifier intead, one must use a comma or a semicolon to explicitly end the expression. Yes, Syntactic sugar causes cancer of the semicolon .

For example, one must use a semicolon in the following exmple to prevent the first Int to be a tag for the second typedef:

typedef PortNumber : Int;

typedef ErrorCode : Int;


Harpoon has operators which can be unary (prefix or postfix) or binary (infix). As they are also only a syntactic sugar, they do not introduce additional constructs. They are just another way to express tagged tuples. For example the following expressions are equivalent:

a + b    // sugared

+(a, b)  // canonical

For operators, designing one syntax to rule them all is not straightforward. We need an universal set of available operators and we have to define their priorities, all in some very general way.

Operator tokens

Therefore, in Harpoon operators are no strictly predefined. In fact, they are defined similarly to identifiers - they are tokens composed of some predefined characters, namely of:

! % ^ & * - + = < > . | ~ ? :

with the exceptions of single-character operators '=' and ':', which are reserved (in the same way as usually some identifiers are reserved in programming languages).

Additionally, identifiers beginning with an underscore character are treated as operators as well (identifier operators). This makes it possible to have operators for which there are not suitable ASCII symbols available (e.g. many set-theoretic).

Prefix vs infix vs postfix

An operator is prefix, infix or postfix depending on its context.

  • If there is nothing before it, it is prefix (e.g. (++i)).

  • If there is nothing after it, it is postfix (e.g. 5 _m;).

  • If there is something on both sides, it is infix (e.g. a + b).

It can also be just an identifier, if there is nothing on both sides. If two operators appear in sequence, the classification is undefined.

Operator priorities

Programming languages usually define tables of operator priorities. In Harpoon a much simpler approach has been chosen - all operator priorities are equal and all are left-associative. And there are of course grouping brackets (equivalent to single-element tuples, which are stripped during parsing).

Priority table is only defined for various kinds of syntactic sugar:

HighestPrefix tags / operators
.Postfix tags / operators
.Binary operators
.Sugared tuple (:)
LowestFirst prefix tags in sugared tuples


The presented approach is very good in not limiting possible applications of the language. Here are some examples of mathematical expressions:

// Transitivity of inequality

(a < b) && (b < c) => (a < c)

// Some theorem about natural numbers

forall ( x _in N ) : x + 1 > x

// A trigonometric law

(sin(x)^2) + (cos(x)^2) == 1

Besides most obvious mathematical operators, Harpoon is able to express most of the object-oriented programming notations:

// Member access


// Method invocation

stdout.write!("Hello world")

Nevertheless, Harpoon is very general and sometimes expressions are a bit less flexible than in specialized languages:

// Assignment requires brackets

x := (a + 2)

// An alternative exploiting left-associativeness

a + 2 -> x

// Subroutine invocation requires explicit invocation operator

System.Console.Writeln!("Hello World!")

Programming example

All that syntactic sugar makes Harpoon a great syntax even for scripting and programming purposes. For example, a program printing a multiplication table may look similar to this:

function main():


for ( i:Int := 0; i < 10; ++i ):


for ( j:Int := 0; j < 10; ++j ):

printf!( "%d ", i * j )

printf!( "\\n" )


return 0


Note, that such programs in Harpoon are homoiconic, i.e. they are data expressions, that potentially can be manipulated or created with ease and fun. There is no better way to do metaprogramming.

And remember that this is only syntactic sugar. When parsing is finished all you have to deal with are lists, records and tuples.

Square brackets

Warning: This may change

Square brackets are commonly used in programming languages, usually for arrays and generic types. In Harpoon they are another syntactic sugar making pairs (2-tuples) tagged with special tag "[]". The first element of such pair is what stands before [], and the second is what is inside.

For example, the following expressions are equivalent:

a[b, c]        // sugared

[](a, (b, c))  // canonical

where, "[]" in the second line is treated as a special operator.

Note that the square brackets are "glued" with the previous data (if it exists). This makes them different from the casual round brackets, which are "glued" only with tags. This is another pair of equivalent data:

( lambda x : x*x )[10]          // sugared

[]( lambda( x, *(x, x) ), 10 )  // canonical