4.2. Syntax and sentences

Anableps is a language oriented to the creation of complex objects, that is, emphasis is put in terseness and readability of constructions. There are, however control sentences, as is a turing-complete language.

4.2.1. Object creation

New objects from existing classes can be created with expressions:

s1 = box1.north
s2 = box1.north + (25 45m)

New objects are created by setting its class and name:

box b; circle c1;

Additionally, initial setting can take place after a colon (:), using parameters:

box b: .size 20 20 cm: .angle 45 ;

Anyway, existing objects can be changed later, with the set keyword:

box b: .size 39 76 cm
set b: .size 20 20 cm
# box b is now 20x20cm

There are two kinds of parameters: named and unnamed. Named parameters start with the name prepended with a dot: the rest is an expression that will set the value:

circle c: .radius 40 cm

Unnamed arguments contain just the expression:

marker m: .at 10 10 : .marker [bar]
place:
    e.top [parallel] b1.north
    e.top @parallel b1.north

4.2.2. Oneline vs extended sentences

Definitions usually take the form of an one-lined sentence. However, sentences can extend over several lines, encompassing each line one paremeter, using indented lines starting with a colon, like the following example shows:

box e
    : .size 20 20 cm
    :.stroke.color black
        : ..thickness 2pt
    : .material wood.oak

In anableps, any indented line starting with a colon is taken as a parameter definition belonging to the last unindented sentence

Sometimes, the single definition of a parameter could span more than a line. In anableps any indented line starting with a pipe character, is taken as a direct continuation of the previous one. Note that in this case, no new parameter is defined: just more text added to whatever parameter was being defined:

box e:
    : .size 20 20 cm
    : .text this is a text
        | that spans more than one line
        | and this is the way
    : .material wood.oak

Incidentally, the layout object, makes extensive use of this feature, in order to create its first unnamed parameter:

layout l1:
    |  .p1   .p2     .p3
    |
    |  .p4   .p5     .p6
    :

To Remark the difference between both mode, note that in the first sentence, we have placed different parameters in several lines. Each line contains the definition of a parameter. In the second and third examples, a single parameter (.text for the second example and an unnamed paramter as in the case of layout) takes several lines. Accordingly a semicolon (:) is used for the first case, while a pipe (|) is prepended before each line in the second.

4.2.3. New classes

Anableps allows defining new clases:

<code> [stances] </code>

4.2.4. Assignation sentences

Assignation sentences take the form of:

class name : <parameter> : <parameter> : ...

This sentences create an object of the desired class and configure its components

  • class is optional, if name is already known.

parameter can be an unnamed parameter:

class name : value : value

or a named one, using the following syntax:

class name : .name value : .name value :

for example:

cube c: .size 2cm 2cm 2cm : .material wood

Assigns the size and the material to a newly created cube.

Once anableps see a name, everything up to the next non-escaped colon is taken as an expression. If a colon is needed as part of the value, escape it with \:

cube c: .size 2cm 2cm 2cm : .label "this is it: face it"  # this does'nt need  escape, as happens inside quotes
cube d: .size 2cm 2cm 2cm : .label this is it\: face it  # this needs an escape

For lengthy configurations in which each parameter take a different line, braces can be used:

icosahedron i {

    .diameter 20cm
    .color red + blue
}

4.2.4.1. Nested assignations

Sometimes, components contain in turn other components that must be set. This can be achieved in several ways:

Accessing directly each component…:

cube c: .color.r 20 .color.g 20 .color.b 20

Opening a brace:

cube c: .color { .r 20 .g 20 .b 20 }

Or using double dot:

cube c: .color ..r 20 ..g 20 ..b 20

And, of course, setting them later:

cube c: .size 20cm
c.color: .r 20 .g 20 .b 30

4.2.5. Operators

As explained above, operators are always used in the setting of parameters. This is because of the way anableps works, parameter setting is always succedded with a constraint.

Operators in Anableps are introduced with the at sign “@”, although in some cases implicit operators or shortcuts can be used (see below)

There are three kinds of operators, regarding the number of parameters:

  • Unary: .xAxis.range @auto
  • Binary: .north @parallel b2.top
  • Other: @auto(+2)

Each parameter definition needs an operator. If no operator is explicitely written, the @equal operator is assumed. That’s why the standard parameter definitions like:

box b: .size 45cm

appear not having any operator, but nonetheless, work the way they are intended.

4.2.5.1. param-scoped operators

Some operators are global, like parallel, distance, angle. But the mayority of the operators are defined in the classes of their first (or only) operand. In this example, @auto is defined in range class and @parallel in the first operand. When parsing, Anableps creates constraints out of parameters, and then runs the [[p-algorithm]].

4.2.5.2. Shortcuts

For the sake of simplicity, some heavily used operators allow shorter versions, as the following example shows:

box b: .east @parallel box2.west
box b: .east || box2.west  #

box b: .east @distance(12cm) box2.west
box b: .east |12cm| box2.west

box b: .east @angle(15deg) box2.west
box b: .east <15deg> box2.west

List of shortcuts for operators:

  • <angle>: same as @angle. Example lineA <45deg> lineB
  • |distance|: same as @distance or @dist. Example lineA |.| lineB
  • lineA |224| lineB
  • planeA |224~| lineB
  • linea @ lineB
  • lineA |@ lineB tangent
  • lineA |220|@ lineB tangente con distancia

4.2.5.3. Parameters

In some cases operators need to take some simple parameters, in addition of the operands. Those parameters are included inside a parentheses following the operator:

graph g: .xaxis @auto(+2)  # set the x axis to auto but adds two units at each end.

4.2.5.4. Use of !

Some geametrical operators, can work in two ways. For example, two parallel planes can be parallel or antiparallel. That’s why there are two version of each operator, with the difference of a trailing exclamation sign:

set: p1 || p2       # planes are forced to have its normals parallel.
set: p1 ||! p2      # planes are forced to place its normals antiparallel.

4.2.6. Control sentences

As expected, the Anableps language provides several control sentences.

4.2.6.1. Loops

Loops are:

for <var> in <values> [.by <amount>] [.step sentence(s)] :
    <sentences>
end

Ranges are usually defined as an array, a range or a function that returns an array:

for i in 1..100 .by 2:
    print i
end

The optional .by parameter allows adjusting the step. it can be a number or an expression.

The optional .step parameter allows defining a sentence that changes the index value (akin to the third parameter in the clang loops).

4.2.6.1.1. Parallel loops:

Sometimes we want to sweep in parallel throught two different sets example:

for i in 10..20, j in 40..30:
    print i+j
end

If the lengths of the loops are different, the shortest range will cycle throught its values again.

4.2.6.2. Old-fashioned Loops

debo incluir esto?:

for (i=1 ; i<13 ; i++ )
end

4.2.6.2.1. Grouping loops:

Although those are not a syntax feature, but a couple of handy functions, is worth noting that the functions pairs(sequence), triples(sequence) prove to be extremely useful when extracting contiguous pairs:

for i in pairs([0,1,2], cycle)
    print i

(0,1)
(1,2)
(2,0)

The cycle parameter provides a new pair (or two in the case of triples) consisting in the pair (last, first), those closing a loop, for example while traversing a set of points.

4.2.6.3. While loops

While loops repeat some code while a condition is met:

while <condition>:
    <sentences>
then:
    <sentences>
end

the then clause is optional and gets executed only if the while did not exited through a break.

4.2.6.3.1. the .plus parameter

Sometimes it’s useful to go through the while loop one more time, after the condition is no longer met. This can be done with the .plus parameter:

i = 1
while i<2 .plus 2:
    print (i)
    i = i * 2

# produces 1, 2, 4

4.2.6.4. Until loops

Until loops repeat some code until a condition is finally met. They execute at least once:

do:
    #sentences
until condition

Until loops accept the .plus parameter too:

i = 1
do:
    print (i)
    i = i * 2
until i > 2 .plus 2

4.2.6.5. Breaking and continuing inside loops

The usual sentence break breaks from the innermost loop. For breaking from outer loops, you should provide the index-variable name:

for i in 10..20:
    for j in 20..30:
        if j > i: continue
        if j == 5 and i == 3: break i

This code goes all the way to 5, 3 and then continues with 1, 4.

Or a label name, if you’ve defined a label before:

@@label1
for i in 10..20
    for j in 20..30
        if j>i: continue
        if j == 5 and i == 3: break @label1

See labels

The sentence continue interrupts the execution whenever is found and continues in the next iteration. It works exactly like break, allowing index variables, except that if a label is used, it must be found inside a loop:

for i in [1..10]:
    for j in [1..100]:
        # code...
        # code...
        continue i # breaks the j loop, continues the i loop
end

4.2.6.6. Defining ranges

Ranges are defined with brackets and double dots: [<lower value>..<upper value>]. Ranges are closed by the left and open by the right, so [3..6] spans 3, 4 and 5.

4.2.6.7. Decission sentences

The usual if sentence has the following format:

if <condition>:
    <code>
elif <condition>:
    <code>
elif <condition>:
    <code>
else:
    <code>
then:
    <code>
end

The clause elif allows chained else ifs to be compactly written. Each one is executed only if the previous ones were evaluated to false.

The clause then is executed only if the code in the if, elif or else clauses was not interrupted by a break.

Breaking from nested ifs is possible with labels:

if 2y + 3x > 25m: @@label1
    if <condition2>:
        blah blah
    elif <condition3>:
        blah blah
        break label1

4.2.6.8. List comprehensions

List comprehensions allow creating lists in a compact way, or alternatively, achieve the same as some explicit loop in only one line. The syntax is:

[ <expression> for <var> in <range> [by <amount] [and <var> in <range> [by <amount]] [if <condition>] ]

4.2.6.8.1. Parallel comprehensions

Example:

a = [i * 10 + j for i in 10..20 and 20..30 by 4]

4.2.6.8.2. Nested comprehensions

Example:

a = [i * 10 + j for i in 10..20 by 4 and j in 20..30 by 4]

Produces a list [100, 140…]

4.2.7. Functions

4.2.7.1. Function calls

Function call works as follows: first argument follows after name, without semicolon. further arguments and parameters can be added to tune the operation:

hairify box: .length 34 : .thickness 42 : number 1E3

Functions can use the multiline syntax too:

hairify box
    : .length 34
    : .thickness 42
    : .number 1E3

In many cases, functions //transform// its arguments. For example, in the previous example the object becomes a hairified box. Full reference of functions can be found here

4.2.7.1.1. Uniform form call (UFC)

Traditionally, the fields appended to an object are parameters or functions than can be called. Howewever, if the interpreter finds a name that cannot recognize, but there is a global function in the scope that takes that object as first paramater, the form:

object.function: .param1 value1 : .param2 value2 ...

Is converted to:

function: object : .param1 value1: .param2 value2 ...

So, the two following calls are equivalent:

scene1.render: .width 200px
render scene1: .width 200px

This is called Uniform functino call and used extensively (see here and here). It’s very useful providing an unifying syntax for functions that can augment existing objects.

4.2.7.2. Defining functions

blah blah:

def myfunction
    : .name int
    : .size int
    : .width int : ..default 23
    : .code {
        # code for the function
    }

Todo

defining functions

4.2.8. Expressions

blah blah

Todo

syntax of expressions

4.2.9. Other commodities

Todo

other commodities of syntax

4.2.10. the keyword follows

Todo

the keyword follows: where does it belongs?

The following example:

cube c: .p1 q1
cube d: .p1 follows q1   # this means that if q1 changes, p1 will change to

Upon using the keyword follows, the object creates its p1 point, at the same place at q1. Additionally, however, it subscribes p1 to changes in q1, so, if q1 changes (it moves) then p1 will be moved to the same place