This software is designed to produce two-dimensionnal images in PNG format from a source code file. The main idea is that an image is a combination of some parameters such as its size, its background color, and a content. In this language, the content itself of an image is the result of the evaluation of a piece of source code, and then could be assimilated to it.
There are three main usage of this software:
This software has in its design several constraints:
The syntax of each command will be displayed on a black background
, with arguments <within angle brackets and in italic>
and optional parts of the instructions [between squared brackets put in italic]
.
When an optional element can be repeated, a star indicates that [this element can be repeated, and can be absent]*
and a plus indicates that [this element can be repeated, but at least one element should be present]+
.
Programming corresponds to give a succession of orders to a computer using a text file. This file is read by a program installed on the computer and this program will execute the orders. This program often produces another file that is only a translation of the orders given in the text file.
The source code is a text file that contains words, numbers and symbols that shall respect a syntax of a language.
These elements are often arranged to make instructions, which represent the atomic actions in the language. By default, the actions associated to the instructions are executed in the order the instructions are written in the source code. This succession of instruction is generally called execution flow.
Some instructions will modify the execution order. These instructions are called control instructions, as they permit to control the execution flow.
disp 2*(3+5) disp "A"+STRING[3;~03d] return disp 2+2disp (2)*((3)+(5)): 16 disp ("A")+(STRING[3;~03d]): "A003"
Result:
As you can see above, the two first disp instructions where executed in the same order, and the last one was not executed, as it is preceded by a return instruction that terminates the program in this context. In this example, return is a control instruction.
The compiler is the program that will read the source code, make several checks on the code, and if the code is correct, will perform some actions.
Strictly speaking, a compiler will translate the source code in another file with another language. The programs that read the source code line by line and execute directly the actions are called interpretors.
The programmer is you. You will write the source code with your favourite text editor, call the compiler and check the result of the compiler.
Now, let's go deeper in the art of writting source code! As it has previously written in The source code, the text in the source code follows specific rules that define the programming language.
Lexical rules indicate how to write every word in the language.
High level languages define several kind of symbols like numbers, reserved words in the language, identifiers, and comments.
Comments are the only part of the code that will not be read by the compiler. Comments can contain a free text to explain what the code does, or inactivate a part of the code.
This is not a comment //but this is a comment
The constants are values that are directly written in a text shape in the code. The compiler will automatically convert it to the real value.
Depending on the language, several objects can be written as constants: booleans, numbers, character strings containing text.
2 //is an integer constant "Traceur" //is a string constant
A type is a special entity that represents the nature of a value, and also specializes the operations that can involve a value of a given type.
The compiler also uses types to check the validity of the operations written in the source code to prevent illegal operations in the code.
When the compiler detects an invalid operation on a type, it usually stops the compilation and answer with an explicit error with the misused operator.
Integer //is the type that represent integers Boolean //is the type that represent a condition
The role of an identifier is to give a name to a value, to be able to reference it several times in a source code.
A best practice is to give meaningful names to values. For example, bind the name i to an number is not as meaningful as the name point_index.
a_valid_ident1fier_1 //is a valid identifier 1mpossible //is invalid as this one starts with a digit An_iNvalid_one //is invalid as this one contains at least a capital letter (specific to Traceur) i //is invalid as this one is too short (specific to Traceur)
A keyword is a reserved word in the language, and the compiler will trigger built-in actions on keywords.
You will not be able to define an object with a keyword as name.
error an_error disp 3 //keywords are in yellow, other elements are in white
Operators are like keywords, but they represent an explicit operation on values.
Some operators are words, but others are symbols made of a single character that is neither a letter or a digit.
disp 2+3 disp cos (pi/6) //+, cos, pi and / are operators
Syntaxic rules indicate how to assemble the words to make valid sentences in the language, such as each instruction and expression.
if Integer index < 5 then return "OK" end //is a valid syntax then Integer 3 if "KO" return //is not a valid syntax
Semantic rules indicate what is the role of each instruction in the language, and what they do.
The semantic analysis can take place only when the syntax of a language is respected.
In the example of the section Syntax, only the first example can be interpreted as an action: if the integer index is lower than five, the string "OK" is returned.
An expression is a concept taken from mathematical world. Its main goal is to provide a way to write computation on differents values using built-in operators.
The expressions will often be a translitteration of the mathematical ones, with some differents when a mathematical notation can not be written into ASCII plain text.
The functions are a nice way to simplify the writting of large programs, as it allow to:
disp 2+4*5 disp "A" disp cos (pi/5) disp 2+4*8 disp "B" disp cos (pi/8) disp 2+4*6 disp "C" disp cos (pi/6)
let Void display_three_values(Integer integer, String string) := do disp 2+4*Integer integer disp String string disp cos (pi/Integer integer) end disp Void display_three_values(5,"A") disp Void display_three_values(8,"B") disp Void display_three_values(6,"C")
As you can see, the identical code is put in the function definition (the let instruction). The function here works like a pattern that will be applied on three set of values in the last disp instructions. In this case, the function is called from the disp instructions.
A function is made of two elements. The first one is its prototype, working as an interface to the outside of the function. The prototype also represents the type of the function.
The prototype is <return-type> <function-name>
or <return-type> <function-name>(<type> <name>[, <type> <name>]*)
where:
The second part of the function is its value, also called body. The value of a function can be an expression or a single instruction. Both kind of values can make references to the parameters like functions.
Functions can be defined with the let instruction.
A function call is an expression that will execute a function, and substitute the function call by the result of the function.
When the function has some parameters, the function call shall precise some expressions that will be linked to the parameters of the function.
The syntax of a function call is <return-type> <function-name>[(<expression>[, <expression>]*)]
.
The function to be called is automatically resolved by the compiler, by looking for a suitable function defined in the context of the call. The context at a location of the source code is the set of defined functions that can be called. A function with the same return type, the same name, and the same parameters (number of parameters and types of the parameters) will alw
The instructions can be classified in two categories:
The simple instructions are located at one place in the code, often identified by a specific keyword, like the disp instruction in the example above.
The complex intructions are made of several simple instructions with other instructions between them. These simple instructions act like code delimiters.
while Integer integer < 3 //while is the first part of the complex command do //do is a delimiter between two parts of code with a different meaning disp Integer integer let Integer integer := Integer integer+1 end //end terminates the while - do - end complex command
disp is the simplest instruction. It allows the programmer to display the value of an expression in the console.
The syntax is disp <expression>
.
let is the instruction that defines functions.
The syntax to define constant functions is let <type> <name> := <body>
where name is an identifier.
let Integer index := 2 let Integer inner_index := Integer index+1 let String a_text := do disp "text" return "text" end
The syntax to define functions with parameters is let <type> <name>(<type_x> <name_x>[, <type_y> <name_y>]*) := <body>
, where each parameter is declared with a type and a name. The parameters are known in the value as constant functions.
let Integer index(Integer origin, Integer index) := Integer origin+Integer index let String function(Integer a_value, String a_text) := do disp Integer a_value+|String a_text| return "text_"+String a_text end
The if instruction role is to run a list of instructions only if a certain condition is true.
The condition is represented by an expression with a boolean type.
The syntax is if <boolean-expression> then <list-of-instructions-true-condition> [else <list-of-instructions-false-condition>] end
.
When the boolean-expression returns:
let Integer integer := 4 if Integer integer < 5 then disp "Hello world!" end if Integer integer > 3 and Integer integer < 10 then disp Integer integer-3 else disp Integer integer disp "True integer" end if MAYBE then //This code will compile, but an error will be raised at execution time disp 2 enddisp "Hello world!": "Hello world!" disp (Integer integer)-(3): 1 +---------------------------------------------------------+ | File -, line 13 > Runtime error: Maybe condition found. | +---------------------------------------------------------+
Result:
The for instruction role is to execute a list of instructions several times. The number of iterations does not depend on the list of instructions that are executed, as it is determined directly in the command.
The syntax is for <numeric-type> <index-name> from <start-value> to <stop-value> [by <step-value>] do <list-of-instructions> end
.
The type of the index can be Integer, Real or Number. Please be aware of the type of the start, stop and step values. The following example will not compile, because the to value is an integer, and is not convertible to a real number:
for Real index from 1. to 5 do disp Real index end+------------------------------------------------------------------------------------------------------------------------+ | File -, line 1 > Compilation error: Incorrect type Integer in to part of for loop. Type compatible with Real expected. | +------------------------------------------------------------------------------------------------------------------------+
Result:
let Integer total := 0 for Integer index from 1 to 10 by 2 //Integer index will be equal to 1, 3, 5, 7 and 9 do disp (Integer index+1)/2 let Integer total := Integer total+Integer index disp Integer total end disp "Total:" disp Integer totaldisp ((Integer index)+(1))/(2): 1 disp Integer total: 1 disp ((Integer index)+(1))/(2): 2 disp Integer total: 4 disp ((Integer index)+(1))/(2): 3 disp Integer total: 9 disp ((Integer index)+(1))/(2): 4 disp Integer total: 16 disp ((Integer index)+(1))/(2): 5 disp Integer total: 25 disp "Total:": "Total:" disp Integer total: 25
Result:
The while instruction is a loop instruction, like the for instruction, with a small difference: the number of iterations is not known in advance. The instructions in the loop will be executed while a certain condition is true.
The syntax is while <boolean-expression> do <list-of-instructions> end
.
Before each execution of the list-of-instructions, the boolean-expression is evaluated. When this expression returns:
let Integer randsum := 0 let Integer nbiterations := 0 while Integer randsum <= 100 do let Integer randsum := Integer randsum+(rand%10) let Integer nbiterations := Integer nbiterations+1 end disp Integer nbiterationsdisp Integer nbiterations: 20
Result:
As the condition is determined by the contents of the loop, you must ensure that the condition becomes false on at least one iteration. If you are not careful about this, the execution can be stuck in an infinite loop.
If the first time the condition is evaluated, the value is FALSE, the list-of-instructions is not executed.
The return instruction stops the current flow of execution and raise a value to the the upper context. The execution restarts at the level of the latest function call. Its main role is to return the result value of a function.
The syntax is return [expression]
. If the expression is not present, the void expression is returned.
The compiler will raise an error when a function with a non void return type can reach the end of its value without any return instruction.
let Integer function(Boolean param) := if Boolean param then return 1 end+-------------------------------------------------------------------------------------------------------------+ | File -, line 1 > Compilation error: Missing return instruction in function Integer function(Boolean param). | +-------------------------------------------------------------------------------------------------------------+
Result:
An image is a two-dimensional array of pixels having as value a color. The vertical and horizontal numbers of pixels are called size, and is a characteristic of the image. Before any drawing, an image has each pixel set to a default color called background color.
To manipulate images, some specific instructions are needed. Some are dedicated to modify the content of the image, when others will do an operation on the whole image.
The with instruction will change the color used to draw on images.
The draw instruction will take an argument and write on the image the result of this argument.
This argument can be void or can be a graphical tool such as a point, a line or another graphical tool.
When an image is defined, a normal operation is to write it to a file. The write instruction will take an image and a string containing the file name and will write this image to the file.
A color can be defined in two ways. The first one is purely formal, considering that a color is a combination of values called channels. A color can be represented by a red channel, a blue channel and a green channel, with integer values between 0 and 255. It can also represented by a hue, a saturation and a value. In the last system, a saturation of 100% leads to a color with the full hue, and a saturation to 0% leads to a white color. In the same way, a value of 100% leads to a full color, and a value of 0% leads to black color.
The second way is linked to the language of the traceur. A color is the value linked to a pixel in an image. The traceur has a RGB (Red, Green, Blue) representation with a fourth channel that represents the opacity of the color. This last channel is called alpha. This channel is not part of the color, and you should care about the color when the opacity is set to 0, meaning full transparency. For example, a full transparent black is not the same color as a full transparent white.
Traceur is a compiler that will directly execute the object code contained in its internal memory, right after the compilation and link edition phases.
The compiler is invocated by the command "traceur" followed by the list of source code files to compile. The compiler will compile all files before executing the code.
The character - will force the compiler to read from its standard input. If a file parameter starts with the character =, this character will be replaced by the first existing directory in this order:
Invoked without any parameters, the compiler will read from its standard input.
Comments starts with // or #! and ends at the end of the line.
TRUE
, FALSE
and MAYBE
. The last value is the undetermined boolean;In Traceur, only built-in types are possible. It will be impossible to define user types. All types names begin with a capital letter.
The generic types are:
Integer
, Real
and Number
. A Number can represent an integer or a floating point number;Boolean
, String
;Void
.An identifier starts with a low case letter, contains at least five characters, and can be made of low case letters, digits and underscores.
Keywords of Traceur: let
, rec
, func
, :=
, disp
, do
, end
, if
, then
, elif
, else
, for
, from
, to
, by
, while
, until
, next
, return
, exit
, error
, try
, catch
.
Note that only while
, until
, return
, error
and catch
are in conflict with identifier namespace.
Operators are: +
, -
, *
, /
, %
, ^
, °
or deg
, rad
, cos
, sin
, tan
, acos
, asin
, atan
, exp
, ln
, sqrt
, pi
, rand
, min
, max
, gcd
, lcm
, |
value |
, int
, frac
, not
, and
, or
, xor
, =
or ==
, <>
or !=
, <
, <=
, >
, >=
, CATCH
.
Constructors are specific words all in capital letters used to build values of a specific type from other values.
The syntax of constructors is <CONSTRUCTOR>[[<parameter>[; <parameter>]*]]
.
Supported constructors are: TRUE
, FALSE
, MAYBE
, INTEGER
, REAL
, STRING
.
The syntax of constant values are quite important, as the compiler will deduce their type from it:
Operators can be prefixed, infixed or postfixed depending on the operator itself. They are separated from theirs arguments by a space, and arguments does not need to be put inside parenthesis.
A function is made of a prototype linked to a value that can be parametrized.
The syntax of the prototype is one of those two possibilities:
<type> [(<prototype>[, <prototype>]*)]<identifier>[(<prototype>[, <prototype>]*)]
[<prototype>] [(<prototype>[, <prototype>]*)]<identifier>[(<prototype>[, <prototype>]*)]
The identifier is the function name, and the function can take left and/or right parameters. The return value of a function can be a value (first line) or a function (second line).
There are two kind of functions:
func <prototype>
. The link to the function is implicit, the compiler will look for the defined function with exactly the same prototype.let [Integer function(Integer param)] function(Integer param) := do let Integer local(Number param1) := Integer param let Integer local(Integer param1) := Integer param*(1+Integer param1) return func Integer local(Integer param) //imaginary function definition linked to the second local function end let Integer (Integer local(Integer param))function(Integer param1, Integer param2) := if Integer param1 < Integer param2 then return Integer local(Integer param1)+Integer param2 else return Integer local(Integer param2) end disp Integer ([Integer function(Integer param)] function(2))function(3,5)disp Integer ([Integer function(Integer)] function(2))function(3,5): 13
Result:
The syntax of a function call is <return-type> [(<expression>[, <expression>]*)]<function-name>[(<expression>[, <expression>]*)]
.
The function that will be called is the nearest function with the same name and number of parameters on each side, with the less type conversion possible on the parameters and the return type.
disp <expression>
will print the expression and its result on the standard output of the compiler.
disp
without argument will print on the standard error the execution back trace with function calls, and for each frame, will display the value of each defined function.
let is the instruction that defines functions. Its syntax is let <prototype> := <body>
.
The value can be an expression or a single instruction. The return type of the value shall match the return type of the prototype.
The prototype structure is defined in Function definition.
To define a recursive function, the keyword rec need to be added to the let instruction: let rec <prototype> := <body>
.
Please note that the context outside a recursive function is not accessible from its body, and no imaginary function can be linked to a recursive function inside its definition. It is also impossible to redefine a function with the same prototype of a recursive function within let
and let
rec
instructions inside the recursive function definition.
The role of the do instruction is to reduce the scope of function declaration and redeclaration. The syntax of this instruction is do [<list-of-instructions>] end
.
let Integer index := 1 //instance one disp Integer index //index instance one contains 1 do disp Integer index //index instance one contains 1 let Integer index := 2 //instance two disp Integer index //index instance two contains 2 let Integer index2 := 3 //instance three disp Integer index2 //index instance three contains 3 end disp Integer index //index instance one contains 1 //Integer index2 is unknown heredisp Integer index: 1 disp Integer index: 1 disp Integer index: 2 disp Integer index2: 3 disp Integer index: 1
Result:
The do instruction will also agregate several instructions in one. It is useful when defining a function, as it allows function bodies to be more complex than a real simple instruction.
The syntax of the if instruction is if <boolean-expression> then <list-of-instructions-true-condition> [elif <alternate-boolean-condition> then <list-of-instructions-alternate-condition>]* [else <list-of-instructions-false-condition>] end
.
This structure will play the role of the basic if-then-else instruction, and the role of the switch-case instruction of several languages. The only difference is that all conditions must be explicitly written.
When several conditions are present in the instruction, the first clause found with a true condition is executed. If no condition equals true, and a else clause is present, this clause is executed.
If a condition is evaluated to MAYBE, the runtime error maybe_condition_found is raised.
The syntax of the for instruction is for <numeric-type> <index-name> from <start-value> to <stop-value> [by <step-value>] do <list-of-instructions> end
.
The start value, stop value, and step value are evaluated only once, before running the for loop. If the start value is greater than the stop value, with a positive step value, the loop will not be executed.
The keywords do and end are parts of the instruction, and are not a do instruction. The functions defined outside the for loop can be modified inside the loop.
The syntax of the while loop is while <boolean-expression> do <list-of-instructions> end
.
The condition is evaluated before the instructions, and the list-of-instructions is executed while the condition is true. When the boolean-expression becomes MAYBE, a runtime error maybe_condition_found is raised during the execution.
The syntax of the until loop is do <list-of-instructions> until <boolean-expression> end
.
The condition is evaluated after the instructions, and the list-of-instructions is executed until the boolean-expression is true. The list-of-instructions is executed at least once with this instruction. When the boolean-expression becomes MAYBE, a runtime error maybe_condition_found is raised during the execution.
The syntax of the try instruction is try <list-of-instructions-try> [catch(<name-of-error>[, <name-of-error>]*) <list-of-instructions-specific-catch>]* [catch <list-of-instructions-generic-catch>] end
with at least one catch clause.
The try instruction will execute the list-of-instructions-try. If no runtime error is raised, the execution continues after the end of the instruction. Otherwise, the first catch clause that contains the name of the raised error will be executed. If no specific catch clause is found and the generic clause is present, this clause is executed.
The name of an error can be easily found from the error message. For example,
+---------------------------------------------------+ | File -, line 1 > Runtime error: Division by zero. | +---------------------------------------------------+
is the division_by_zero
runtime error. The name is deduced from the error message, putting a low case letter at the beginning, and replacing all spaces by an underscore.
The syntax of the return instruction is return [<expression>]
.
When the expression is missing, the void expression is returned. All functions with non void return type shall terminate on an interruption instruction. When it is not the case, a compilation error is raised by the compiler.
The expression of the return instruction can be an imaginary Function definition.
The syntax of the error instruction is error <error-name>
, where an error name is an identifier. This identifier shall not be declared, and has no relation with function names.
The error will be raised like an error from an operator or an instruction, and can be catched with the try catch instruction.
The syntax of the exit instruction is exit [<level>]
, where level is a positive integer constant. When the level is not given, one is taken by default.
The exit instruction will immediately exit every nested for, while, and until loop instruction. The number of exited loop equals to the level given in argument.
One exception: if zero is given in argument, the execution ends immediately without error.
The syntax of the next instruction is next [<level>]
, where level is a positive integer constant. When the level is not given, one is taken by default.
The next instruction will immediately jump to the next iteration of the n-th nested for, while, and until loop instruction, where n is the level given in argument.
The syntax of an image is IMAGE[<first-size>;<second-size>] [<coordinate-system>] [<background-color>] { <content> }
where the sizes are integer, the coordinate system is a special concept having its own constructors and the content is a sequence of instructions.
The images have their own type: Image
.
Kernels are special bitmaps used for the convolution instruction. Kernels has odd sizes, no background color, and the values of the color and alpha channels are real numbers initialised to zero.
The syntax of a kernel is quite similar to images: KERNEL[<first-size>;<second-size>] [<coordinate-system>] { <content> }
.
Kernels have their own type: Kernel
.
The geometry coordinate system is the default one. The origin is set to the center of the image, the first axis is horizontal, oriented to the right, and the second axis is vertical, to the upper side of the image. The center of each pixel has an integral coordinate on both axis.
When explicitely mentionned, the syntax of this coordinate system is GEOMETRY
.
The standard coordinate system has its origin set to the top left corner of the image, the first axis is vertical, oriented to the bottom, and the second axis is horizontal, to the right side of the image. The center of each pixel has an integral coordinate on both axis.
The syntax of this coordinate system is STANDARD
.
The mathematical coordinate system allows the programmer to define the coordinates of the center of the bottom-left and top-right pixels of the image.
This coordinate system has two possible syntax: MATHEMATICAL[<Point bottom-left>;<Point top-right>]
or MATHEMATICAL[<Number xmin>;<Number xmax>;<Number ymin>;<Number ymax>]
While defining the content of an image, the drawing instructions will use a color defined by the context of the instruction. The with instruction will modify the current color on the current image.
The syntax the the with instruction is with <Color new_color>
.
In order to draw on images and kernels, the draw instruction should be used. Depending on the arguments, draw can do several operations:
The syntax of the draw command is draw <expression>
or draw <image> at <position> [by <alpha>]
.
The write instruction will write an image to a file in the PNG format. The files will be written only if no error is raised by the program.
The syntax is write <image> to <filename>
where the file name is a string with the .png extension.
The read instruction will open the file named by the instruction argument, and will replace the content of the bitmap of the current image by the bitmap contained in the file.
The syntax is read from <filename>
where the file name is a string containing the path and whole file name of the image to read.
The conv instruction will compute the convolution on the current image by the kernel given in argument. All the pixels outside the image will have the background color as value for the computation of the convolution. The result of the convolution replaces the content of the current image.
The syntax of the conv instruction is conv <kernel>
.
The for instruction can be used with a point as index value. The start value and end value are then points and the step is a vector.
In this case, the for loop will be equivalent to two for loops, the outer one on the first coordinate, the inner one on the second coordinate. This loop will not be considered as two loops for the next and exit instructions.
The syntax is for Point <index-name> from <start-point> to <stop-point> [by <step-vector>] do <list-of-instructions> end
When invoqued with the argument 0, the next instruction will terminate the code that defines the current image. The next executed instruction is the one right after the end of the image.
The syntax is next 0
.
A point is the representation in the language of a pixel on one image. The coordinates of a point is linked to the coordinates system of the current image, and are linked to the center of each pixel in the image.
A point has its own type Point
, and has two constructors: POINT[<first coordinate>; <second coordinate>]
where each coordinate is a number, and POINT[<vector>]
.
The meaning of the first and second coordinates depends on the coordinates system of the current image.
A vector is a pure geometrical entity like in the mathematical world. It supports some basic geometrical operations such as the Chasles operations, homothetias, symetries, projections and rotations.
A vector has its own type Vector
, and has several constructors:
VECTOR[<first cartesian coordinate>; <second cartesian coordinate>]
constructs a cartesian vector with two number coordinates,VECTOR[<point>]
constructs a cartesian vector using the coordinates of the given point,VECTOR[<vector>]
constructs a cartesian vector from another cartesian vector or a polar vector,POLAR[<module>; <argument>]
constructs a polar vector by its module and its argument in radians,POLAR[<point>]
constructs a polar vector with its module equals to the distance from the origin to the point, and the angle between the first axis in the coordinates system to the direction of the point,POLAR[<vector>]
constructs a polar vector from another polar vector or a cartesian vector.The vector operators are the common operations between vectors:
Operator | Return type | Comment |
---|---|---|
cos <Point> |
Number |
First cartesian coordinate |
sin <Point> |
Number |
Second cartesian coordinate |
| <Point> | |
Real |
Distance to the origin |
atan <Point> |
Real |
Angle in radians between the first axis and the point |
<Point> = <Point> |
Boolean |
Return TRUE when the two points are the same pixel on the current image |
<Point> <> <Point> |
Boolean |
Return TRUE when the two points are not the same pixel on the current image |
The vector operators are the common operations between vectors:
Operator | Return type | Comment |
---|---|---|
+ <Vector> |
Vector |
Identity |
- <Vector> |
Vector |
Opposite |
<Vector> + <Vector> |
Vector |
Chasles operation |
<Vector> - <Vector> |
Vector |
Chasles operation |
<Vector> * <Vector> |
Number |
Scalar product |
<Vector> ^ <Vector> |
Number |
Norm of the vectorial product |
cos <Vector> |
Number |
First cartesian coordinate |
sin <Vector> |
Number |
Second cartesian coordinate |
| <Vector> | |
Real |
Norm of the vector |
atan <Vector> |
Real |
Argument of the vector |
<Vector> = <Vector> |
Boolean |
Return TRUE when the two vectors have the same cartesian coordinates |
<Vector> <> <Vector> |
Boolean |
Return TRUE when the two have not the same cartesian coordinates |
Transformations are operations on vectors and points that have a meaning in geometry:
Operators | Return type | Comment |
---|---|---|
<Point> + <Vector> |
Point |
Translation |
<Point> - <Point> |
Vector |
Offset |
<Vector> * <Number> |
Vector |
Homothety |
<Vector> / <Number> |
Vector |
Homothety |
<Number> * <Vector> |
Vector |
Homothety |
<Vector> ^ <Number> |
Vector |
Rotation with angle in radians |
<Vector> / <Vector> |
Vector |
Projection on the second vector |
<Vector> % <Vector> |
Vector |
Symetry by the second vector |
The traceur uses a RVBA representation of colors with 8 bits depth, and can handle the HSVA representation as well.
The color on a kernel is a RVBA representation with real numbers instead of 8 bits channels.
The color has it own type Color
and has several constructors:
COLOR[<red channel>; <green channel>; <blue channel>; <alpha channel>]
where all arguments are numbers. All numbers above 255 or under 0 are truncated,COLOR[<color>]
constructs a color in RGBA mode from another color in RGBA mode or in HSVA mode,COLOR[<color>; <alpha channel>]
is used to change the alpha channel of the given color structure,COLOR[<point>]
returns the RGBA color of the given point on the current image,#<HTML RGB hexadecimal code>
is a short way to define a color constant. To change the alpha channel, please refer to the constructor above,HSV[<hue>; <saturation>; <value>; <alpha channel>]
where all arguments are numbers. Hue is between 0 and 359, saturation and value between 0 and 100,HSV[<color>]
will construct a color in HSVA mode from another HSVA color or a RGBA color. When the color is black, grey or white, the hue is set by default to 0, meaning red,CLEAR
is the transparent black,BLACK
, GRAY
, WHITE
, RED
, GREEN
, BLUE
, YELLOW
, CYAN
, MAGENTA
are constructors for the corresponding colors.Colors can be computed using some operators:
Operator | Return type | Comment |
---|---|---|
<Color> + <Number> |
Color |
Increase saturation |
<Color> - <Number> |
Color |
Decrease saturation |
<Color> * <Number> |
Color |
Increase value |
<Color> / <Number> |
Color |
Decrease value |
<Color> % <Number> |
Color |
Increase alpha |
<Color> ^ <Number> |
Color |
Change hue, with angle in degrees |
<Color> - <Color> |
Number |
Distance between two colors |
| <Color> | |
Color |
Equivalent color in grey level |
<Color> / <Color> |
Color |
Resulting color after application of the first one on the second one |
RED[<color>] |
Number |
Returns the red channel of the given color |
GREEN[<color>] |
Number |
Returns the green channel of the given color |
BLUE[<color>] |
Number |
Returns the blue channel of the given color |
ALPHA[<color>] |
Number |
Returns the alpha channel of the given color |
HUE[<color>] |
Number |
Returns the hue of the given color |
SATURATION[<color>] |
Number |
Returns the saturation of the given color |
VALUE[<color>] |
Number |
Returns the value of the given color |
The operators that increases or decreases the saturation and the value work in the opposite way when the number is negative.
The drawing tools are special expressions that can be used by the draw
instruction to draw some specific elements to the current image.
All tools can be designed under the type Tool
and each tool has its own type that is compatible with the generic tool type.
Each time options are availables for a tool, they are written after the last argument, separated from the other arguments by a semi-colon, and are separated by a coma between options. When options have their own parameters, the syntax is the same as the constructors.
The draw
instruction can take a point as argument, and will draw a dot on the image at the given coordinates.
There is no option available for this tool.
The solid tool has its own type , and has one constructor:
SOLID[<point>]
.
This tool will draw a dot, by replacing the color by the current color, instead of applying it on the previous color.
There is no option available for this tool.
The eraser tool has its own type Eraser
, and has one constructor: ERASER[<point>]
.
This tool will draw a dot, by replacing the color by the background color, instead of applying the current color on the previous color.
There is no option available for this tool.
The fill tool has its own type Fill
, and has one constructor: FILL[<point>]
.
This tool will fill a surface from the point given in argument. The surface that will be colored is the smallest contiguous region with pixels of the same color of the color of the pixel at the given point before drawing the current color.
There are three options available:
SOLID
replaces the color by the current color instead of applying it on the previous color,ERASER
replaces the color by the background color,FILL
will fill a surface delimited by the current color. It will replace all pixels until it finds the current color.The line tool has its own type Line
, and has two constructors:
LINE[<start point>; <end point>]
will draw a segment between the two points,LINE[<start point>; <vector>]
will draw a segment from the point to the same point translated by the vector.There are some options for line based tools:
NOSTART
will not draw the first pixel of the line,NOEND
will not draw the last pixel of the line,SOLID
will replace the color by the current color,ERASER
will replace the color by the background color,DASHED[<drawed pixels> [; <non drawed pixels>]]
where both arguments are integers. When the second one is not given, it is considered as equal to the first one,DOTTED
is equivalent to DASHED[1]
.The triangle tool has its own type Triangle
, and has two constructors:
TRIANGLE[<first point>; <second point>; <third point>]
will draw a triangle using the three points as vertices,TRIANGLE[<first point>; <first edge>; <second edge>]
will draw a triangle using the the first point as a vertex, and the two other arguments as vector to compute the two remaining vertices by translation of the first argument.The rectangle tool has its own type Rectangle
, and has several constructors:
RECTANGLE[<first point>; <second point>]
will draw a rectangle using the two points as opposite vertices, with edges parallel to the axis,RECTANGLE[<point>; <vector>]
will draw the same rectangle as above, but the vector is a diagonal of the rectangle,RECTANGLE[<first point>; <second point>; <number>]
will draw a rectangle using the two points as vertices of the same edge, and using the number as the length of the others edges,RECTANGLE[<point>; <vector>; <number>]
will draw the same rectangle as above, but using the vector as an edge.The circle tool has its own type Circle
, and has several constructors:
CIRCLE[<center>; <radius>]
will draw a circle centered where the radius is a number. The radius of the circle is considered as the length of a vector on the first axis,CIRCLE[<center>; <vector>]
will draw a circle centered where the radius is a vector. The norm of the vector is taken as the radius,CIRCLE[<center>; <point>]
will draw a circle centered passing through the second point.There are three options available for all surface based tools:
SOLID
replaces the color by the current color instead of applying it on the previous color,ERASER
replaces the color by the background color,FILL
will draw the interior of the element as well.No array and no map are defined in Traceur language. As Traceur is function oriented, both structures will be represented by functions.
let String an_array(Integer index) := if Integer index = 0 then return "zero" elif Integer index = 1 then return "one" elif Integer index = 2 then return "two" elif Integer index = 3 then return "three" else error out_of_range end
let Point a_map(String a_key) := if String a_key = "origin" then return POINT[0;0] elif String a_key = "x-axis" or String a_key = "x" then return POINT[xmax-1;0] elif String a_key = "y-axis" or String a_key = "y" then return POINT[0;ymax-1] elif String a_key = "opposite" then return POINT[xmax-1;ymax-1] else error value_not_mapped end
The returned value can be partially computed from the key, like every function. Only a prototype that can be identified to an array or a map is sufficient to reproduce the behaviour of these structures.
Concerned operators are:
pi
,rand
.Syntax | Return type | Comment | Errors |
---|---|---|---|
pi |
Real |
||
rand |
Integer |
Concerned operators are:
-
,-
,|
|
,deg
,rad
,int
,frac
.Syntax | Return type | Comment | Errors |
---|---|---|---|
|
Integer |
||
|
Real |
||
|
Number |
||
|
Integer |
||
|
Real |
||
|
Number |
||
int <Integer> |
Integer |
||
int <Real> |
Real |
||
int <Number> |
Number |
||
frac <Integer> |
Integer |
||
frac <Real> |
Real |
||
frac <Number> |
Number |
||
<Integer> deg |
Integer |
Converts angle in degrees to radians. | |
<Real> deg |
Real |
Converts angle in degrees to radians. | |
<Number> deg |
Number |
Converts angle in degrees to radians. | |
<Integer> rad |
Integer |
Converts angle in radians to degrees. | |
<Real> rad |
Real |
Converts angle in radians to degrees. | |
<Number> rad |
Number |
Converts angle in radians to degrees. | |
|
Integer |
||
|
Real |
||
|
Number |
||
|
Number |
String length. |
All mathematical operators are prefixed. Concerned operators are:
cos
,sin
,tan
,acos
,asin
,atan
,exp
,ln
,sqrt
.Returned type:
Argument | Returned type |
---|---|
<Integer> |
Real |
<Real> |
Real |
<Number> |
Real |
All these operators raise out_of_range when the argument is outside its mathematical definition range.
All binary operators are infixed. Concerned operators are:
+
,-
,*
,/
,%
,^
,min
,max
.Returned type:
left\right | <Integer> |
<Real> |
<Number> |
<String> |
---|---|---|---|---|
<Integer> |
Integer |
Real |
Number |
|
<Real> |
Real |
Real |
Number |
|
<Number> |
Number |
Number |
Number |
|
<String> |
String |
Special cases:
/
with numeric parameters raises error division_by_zero
when the second parameter is 0,%
with numeric parameters raises error remainder_by_zero
when the second parameter is 0,Syntax | Return type | Comment | Errors |
---|---|---|---|
<String> |
String |
Concatenation of strings. | |
<String> |
Integer |
ASCII code of the character at the given position. Starts at 0. | Raise error out_of_range if the index is negative or taller than the string size. |
<String> |
String |
Matches the left string against the POSIX regex contained in the second string. Returns the first match. | |
<String> |
String |
Matches the left string against the POSIX regex contained in the second string. Returns the trailer after the first match. |
All binary operators are infixed. Concerned operators are:
gcd
,lcm
.Syntax | Return type | Comment | Errors |
---|---|---|---|
<Integer> gcd <Integer> |
Integer |
||
<Integer> lcm <Integer> |
Integer |
Concerned operators are:
not
,and
,or
,xor
.Returned type:
Syntax | Return type | Comment | Errors |
---|---|---|---|
not <Boolean> |
Boolean |
||
<Boolean> and <Boolean> |
Boolean |
||
<Boolean> or <Boolean> |
Boolean |
||
<Boolean> xor <Boolean> |
Boolean |
Logical table:
<A> |
<B> |
not <A> |
<A> and <B> |
<A> or <B> |
<A> xor <B> |
---|---|---|---|---|---|
TRUE |
TRUE |
FALSE |
TRUE |
TRUE |
FALSE |
TRUE |
FALSE |
FALSE |
FALSE |
TRUE |
TRUE |
FALSE |
TRUE |
TRUE |
FALSE |
TRUE |
TRUE |
FALSE |
FALSE |
TRUE |
FALSE |
FALSE |
FALSE |
TRUE |
MAYBE |
FALSE |
MAYBE |
TRUE |
MAYBE |
FALSE |
MAYBE |
TRUE |
FALSE |
MAYBE |
MAYBE |
MAYBE |
TRUE |
MAYBE |
MAYBE |
TRUE |
MAYBE |
MAYBE |
FALSE |
MAYBE |
FALSE |
MAYBE |
MAYBE |
MAYBE |
MAYBE |
MAYBE |
MAYBE |
MAYBE |
MAYBE |
All binary operators are infixed. Concerned operators are:
=
,<>
,<
,<=
,>
,>=
.Returned type:
left\right | <Integer> |
<Real> |
<Number> |
<String> |
---|---|---|---|---|
<Integer> |
Boolean |
Boolean |
Boolean |
|
<Real> |
Boolean |
Boolean |
Boolean |
|
<Number> |
Boolean |
Boolean |
Boolean |
|
<String> |
Boolean |
Special cases:
Syntax | Return type | Comment | Errors |
---|---|---|---|
<Boolean> |
Boolean |
||
<Boolean> |
Boolean |
The catch operator will evaluate an expression, and if this expression raises an error, will replace this error by the result of the evaluation of an alternate expression.
The catch operator can catch a specific error, or catch all possible errors. The type returned by the alternate expression shall be compatible with the normal expression.
The syntax of the catch operator with specific error is CATCH[<expression>; <name of the error>; <alternate expression>]
.
The syntax of the catch operator with any error is CATCH[<expression>; <alternate expression>]
.
Syntax | Return type | Comment | Errors |
---|---|---|---|
TRUE |
Boolean |
||
FALSE |
Boolean |
||
MAYBE |
Boolean |
Neither true or false. | |
INTEGER[<Integer>] |
Integer |
||
INTEGER[<Real>] |
Integer |
Build the nearest integer. | |
INTEGER[<Number>] |
Integer |
Build the nearest integer when the parameter is a real value. | |
INTEGER[<String>] |
Integer |
Build integer with litteral value. | Raise error invalid_integer_conversion if the string does not represent an integer. |
REAL[<Integer>] |
Real |
||
REAL[<Real>] |
Real |
||
REAL[<Number>] |
Real |
||
REAL[<String>] |
Real |
Build real with litteral value. | Raise error invalid_real_conversion if the string does not represent a real. |
STRING[<Integer>;<format>] |
String |
Integer format is ~i , ~d , ~o , ~x , ~X with printf options. |
Raise error invalid_format if the first argument is not an integer. |
STRING[<Real>;<format>] |
String |
Real format is ~f , ~F , ~e , ~E with printf options. |
Raise error invalid_format if the first argument is not a real. |
STRING[<Number>;<format>] |
String |
Try to build the string from a number. | Raise error invalid_format if the first argument does not correspond to the format. |
STRING[<String>;<format>] |
String |
String format is ~s . |
Raise error invalid_format if the first argument is not a string. |
Let see deeper in imaginary functions. A value can in fact be bound to an imaginary function during its creation. The syntax of the imaginary function definition with a body is func <prototype> := <body>
. In this case, two functions are defined, a real one and an imaginary one, linked to the real one. This imaginary function scope is the instruction where it is defined, like a lambda function.
The value can have access to the parameters defined in the prototype, and the real function implied here can be recursive. To define a recursive lambda function, the syntax is func rec <prototype> := <body>
.
let [Integer function(Integer param)] function(Integer param) := return func Integer local(Integer param1) := Integer param*(1+Integer param1) let Integer (Integer local(Integer param))function(Integer param1, Integer param2) := if Integer param1 < Integer param2 then return Integer local(Integer param1)+Integer param2 else return Integer local(Integer param2) end disp Integer ([Integer function(Integer param)] function(2))function(3,5) disp Integer (func Integer lambda(Integer param) := do disp "lambda function called" return 12+5*Integer param end )function(3,5)disp Integer ([Integer function(Integer)] function(2))function(3,5): 13 disp "lambda function called": "lambda function called" disp Integer (func Integer lambda(Integer param) := do disp "lambda function called" return (12)+((5)*(Integer param)) end )function(3,5): 32
Result:
There are two ways of passing functions in parameter of another function:
First, a function is compatible to a function call when in particular each types of the argument expressions are compatible with the return type of each function implied by each parameter. For instance, a parameter declared as <parameter-type> <parameter-name>
will accept as value the result of an expression of type <parameter-type>
. In the same way, to pass a function to another, the parameter should be declared as [<prototype>] <parameter-name>
. In this case, the call to this parameter inside the function body will not execute the function returned by the parameter. In order to do this, the value of the function need to be bound to a function inside the body using let func:
let [Integer temp_func(Integer param)] generator(Integer root_value) := return func Integer inner_func(Integer param) := 1+Integer root_value*Integer param let Void ([Integer function(Integer a_param)] operator)display(Integer value) := do let func Integer operator(Integer a_param) := [Integer function(Integer a_param)] operator disp Integer operator(Integer value) end disp Void ([Integer temp_func(Integer param)] generator(2))display(3)disp Integer operator(Integer value): 7 disp Void ([Integer temp_func(Integer)] generator(2))display(3):
Result:
As you can see above, the formal way, even it is really logical, is not really convenient. Another syntax is also possible. This syntax will allow the compiler to bind the result of the argument expression to the function parameter. This result function will be automatically known inside the body of the function:
let [Integer temp_func(Integer param)] generator(Integer root_value) := return func Integer inner_func(Integer param) := 1+Integer root_value*Integer param let Void (Integer operator(Integer a_param))display(Integer value) := do disp Integer operator(Integer value) end disp Void ([Integer temp_func(Integer param)] generator(2))display(3)disp Integer operator(Integer value): 7 disp Void ([Integer temp_func(Integer)] generator(2))display(3):
Result:
The syntax of the let func instruction is let func <prototype> := <body>
.
The difference with the simple let (Developer) is on the the match between the prototype and the return type of the value. In the simple let instruction, the return type of the value must match the return type of the prototype. In a let func instruction, this return type shall match the whole prototype. The value is an instruction or an expression, and can not call the parameters defined in the prototype.
There are two main usages of the let func instruction:
let Number function(Integer param1, Number param2) := Integer param1 + Number param2 let Number function(Number param1, Integer param2) := Number param1 - Integer param2 disp Number function(3,3)+--------------------------------------------------------------------------------------------------------------------------+ | File -, line 4 > Compilation error: Function call Number function(Integer expr, Integer expr) matches several functions: | | - Number function(Integer param1, Number param2) | | - Number function(Number param1, Integer param2) | +--------------------------------------------------------------------------------------------------------------------------+
Result:
let Number function(Integer param1, Number param2) := Integer param1 + Number param2 let Number function(Number param1, Integer param2) := Number param1 - Integer param2 let func Number function(Integer param1, Integer param2) := func Number function(Integer param1, Number param2) disp Number function(3,3.) disp Number function(3,3)disp Number function(3,3.): 6. disp Number function(3,3): 6
Result:
let Integer function(Integer param1(Integer param11), Integer param2) := Integer param1(1+2*Integer param2) let [Integer function(Integer param1)] function(Integer param1) := return func Integer function(Integer param2) := (1+Integer param1)*Integer param2 disp Integer function([Integer function(Integer param1)] function(3),5) let func Integer function(Integer param) := [Integer function(Integer param1)] function(3) disp Integer function(5)disp Integer function([Integer function(Integer)] function(3),5): 44 disp Integer function(5): 20
Result:
When the previous color need to be restored after some drawing, the with instruction can be used with a sequence of instructions, using the syntax with <Color new_color> { <list-of-instructions> }
.