Error messages
Error messages are typically displayed in the form of compiler errors. They occur when the code cannot be successfully compiled and usually indicate issues such as syntax or semantic errors. They can arise at different stages in the compilation process and may include the file and line number where the error occurred (when this information can be retrieved), as well as a brief description of the problem.
The compiler is organized in several stages:
- starting from the DSP source code, the parser builds an internal memory representation of the source program (typically known as an Abstract Syntax Tree) composed here of primitives in the Box language. A first class of error messages is known as syntax errors, like missing the
;character to end a line, etc. - the next step is to evaluate the definition of the
processprogram entry point. This step is basically a λ-calculus interpreter with strict evaluation. The result is a “flat” block diagram where everything has been expanded. The resulting block is type annotated to discover its number of inputs and outputs. - the “flat” block diagram is then translated into the Signal language, where signals are conceptually infinite streams of samples or control values. The Box language actually implements the Faust Block Diagram Algebra, and not following the connection rules will trigger a second class of error messages, the box connection errors. Other errors can be produced at this stage when parameters for some primitives are not of the correct type.
- the pattern-matching metalanguage allows us to algorithmically create and manipulate block-diagram expressions. So a third class of pattern-matching coding errors can occur at this level.
- signal expressions are optimized and type annotated (to associate an integer or real type with each signal, and to determine when signals must be computed: at init time, control rate, or sample rate) to produce a so-called normal form. A fourth class of parameter range errors or typing errors can occur at this level, such as using delays with an unbounded size.
- signal expressions are then converted into FIR (Faust Imperative Representation), a representation for state-based computation (memory access, arithmetic computations, control flow, etc.), before being converted into the final target language (like C/C++, LLVM IR, Rust, WebAssembly, etc.). A fifth class of backend errors can occur at this level, such as unsupported compilation options for a given backend.
Note that the current error message system is still far from perfect, especially when the origin of the error in the DSP source cannot be properly traced. In this case, the file and line number where the error occurred are not displayed, but an internal description of the expression (as a Box or a Signal) is printed.
Syntax errors
These errors happen when the language syntax is not respected. Here are some examples.
The following program:
box1 = 1
box2 = 2;
process = box1,box2;
will produce the following error message:
error.dsp:2 : ERROR : syntax error, unexpected IDENT
It means that an unexpected identifier has been found on line 2 of the file test.dsp. Usually, this error is due to the absence of the semicolon ; at the end of the previous line.
The following program:
t1 = _~(+(1);
2 process = t1 / 2147483647;
will produce the following error message:
errors.dsp:1 : ERROR : syntax error, unexpected ENDDEF
The parser finds the end of the definition (;) while searching for a closing right parenthesis.
The following program:
process = ffunction;
will produce the following error message:
errors.dsp:1 : ERROR : syntax error, unexpected ENDDEF, expecting LPAR
The parser was expecting a left parenthesis. It identified a keyword of the language that requires arguments.
The following program:
process = +)1);
will produce the following error message:
errors.dsp:1 : ERROR : syntax error, unexpected RPAR
An incorrect parenthesis was used.
The following program:
process = <:+;
will produce the following error message:
errors.dsp:1 : ERROR : syntax error, unexpected SPLIT
The <: split operator is not correctly used, and should have been written process = _<:+;.
The following program:
process = foo;
will produce the following error message:
errors.dsp:1 : ERROR : undefined symbol : foo
This happens when an undefined name is used.
Box connection errors
Diagram expressions express how block expressions can be combined to create new ones. The connection rules are precisely defined for each of them and have to be followed for the program to be correct. Remember the operator priority when writing more complex expressions.
The five connections rules
A second category of error messages is returned when block expressions are not correctly connected.
Parallel connection
Combining two blocks A and B in parallel can never produce a box connection error since the 2 blocks are placed one on top of the other, without connections. The inputs of the resulting block-diagram are the inputs of A and B. The outputs of the resulting block-diagram are the outputs of A and B.
Sequential connection error
Combining two blocks A and B in sequence will produce a box connection error if outputs(A) != inputs(B). So for instance the following program:
A = _,_;
B = _,_,_;
process = A : B;
will produce the following error message:
ERROR : sequential composition A:B
The number of outputs [2] of A must be equal to the number of inputs [3] of B
Here A = _,_;
has 2 outputs
while B = _,_,_;
has 3 inputs
Split connection error
Combining two blocks A and B with the split composition will produce a box connection error if the number of inputs of B is not a multiple of the number of outputs of A. So for instance the following program:
A = _,_;
B = _,_,_;
process = A <: B;
will produce the following error message:
ERROR : split composition A<:B
The number of outputs [2] of A must be a divisor of the number of inputs [3] of B
Here A = _,_;
has 2 outputs
while B = _,_,_;
has 3 inputs
Merge connection error
Combining two blocks A and B with the merge composition will produce a box connection error if the number of outputs of A is not a multiple of the number of inputs of B. So for instance the following program:
A = _,_;
B = _,_,_;
process = A :> B;
will produce the following error message:
ERROR : merge composition A:>B
The number of outputs [2] of A must be a multiple of the number of inputs [3] of B
Here A = _,_;
has 2 outputs
while B = _,_,_;
has 3 inputs
Recursive connection error
Combining two blocks A and B with the recursive composition will produce a box connection error if the number of outputs of A is less than the number of inputs of B, or the number of outputs of B is greater than the number of inputs of A (that is, the following connection rule must be respected: ). So for instance the following program:
A = _,_;
B = _,_,_;
process = A ~ B;
will produce the following error message:
ERROR : recursive composition A~B
The number of outputs [2] of A must be at least the number of inputs [3] of B. The number of inputs [2] of A must be at least the number of outputs [3] of B.
Here A = _,_;
has 2 inputs and 2 outputs
while B = _,_,_;
has 3 inputs and 3 outputs
Route connection errors
More complex routing between blocks can also be described using the route primitive. Two different errors can be produced in case of incorrect coding:
process = route(+,8.7,(0,0),(0,1));
will produce the following error message:
ERROR : invalid route expression, first two parameters should be blocks producing a value, third parameter a list of input/output pairs : route(+,8.7f,0,0,0,1)
And the second one when the parameters are not actually numbers:
process = route(9,8.7f,0,0,0,button("foo"));
will produce the following error message:
ERROR : invalid route expression, parameters should be numbers : route(9,8.7f,0,0,0,button("foo"))
Iterative constructions
Iterations are analogous to for(...) loops in other languages and provide a convenient way to automate complex block-diagram constructions. All par, seq, sum, and prod expressions have the same form: they take an identifier as the first parameter, the number of iterations as an integer constant numerical expression as the second parameter, and an arbitrary block diagram as the third parameter.
The example code:
process = par(+, 2, 8);
will produce the following syntax error, since the first parameter is not an identifier:
filename.dsp : xx : ERROR : syntax error, unexpected ADD, expecting IDENT
The example code:
process = par(i, +, 8);
will produce the following error:
filename.dsp : 1 : ERROR : not a constant expression of type : (0->1) : +
Pattern matching errors
The pattern-matching mechanism allows us to algorithmically create and manipulate block-diagram expressions. Pattern-matching coding errors can occur at this level.
Multiple symbol definition error
This error happens when a symbol is defined several times in the DSP file:
ERROR : [file foo.dsp : N] : multiple definitions of symbol 'foo'
Since computations are performed at compile time and the pattern-matching language is Turing complete, even infinite loops can be produced at compile time and should be detected by the compiler.
Loop detection error
The following (somewhat extreme) code:
foo(x) = foo(x);
process = foo;
will produce the following error:
ERROR : stack overflow in eval
and similar kind of infinite loop errors can be produced with more complex code.
[TO COMPLETE]
Signal related errors
Signal expressions are produced from Box expressions, are type annotated, and are finally reduced to a normal form. Some primitives expect their parameters to follow certain constraints, such as being within a specific range or being bounded. The domain of mathematical functions is checked and disallowed operations are flagged.
Automatic type promotion
Some primitives (like route, rdtable, rwtable, etc.) expect arguments with an integer type, which is automatically promoted. That is, the equivalent of int(exp) is internally added and is not necessary in the source code.
Parameter range errors
Soundfile usage error
The soundfile primitive assumes the part number to stay in the [0..255] interval, so for instance the following code:
process = _,0 : soundfile("foo.wav", 2);
will produce the following error:
ERROR : out of range soundfile part number (interval(-1,1,-24) instead of interval(0,255)) in expression : length(soundfile("foo.wav"),IN[0])
Delay primitive error
The delay @ primitive assumes that the delay signal value is bounded, so the following expression:
import("stdfaust.lib");
process = @(ba.time);
will produce the following error:
ERROR : can't compute the min and max values of : proj0(letrec(W0 = (proj0(W0)'+1)))@0+-1
used in delay expression : IN[0]@(proj0(letrec(W0 = (proj0(W0)'+1)))@0+-1)
(probably a recursive signal)
[TO COMPLETE]
Table construction errors
The rdtable primitive can be used to read through a read-only (predefined at initialization time) table. The rwtable primitive can be used to implement a read/write table. Both have a size computed at compile time.
The rdtable primitive assumes that the table content is produced by a processor with 0 input and one output, known at compiled time. So the following expression:
process = rdtable(9, +, 4);
will produce the following error, since the +is not of the correct type:
ERROR : checkInit failed for type RSEVN interval(-2,2,-24)
The same kind of errors will happen when read and write indexes are incorrectly defined in a rwtable primitive.
Duplicate pathname error
Pathnames for different GUI items have to be different. Compiling the following code:
import("stdfaust.lib");
nBands = 8;
filterBank(N) = hgroup("Filter Bank",seq(i,N,oneBand(i)))
with {
oneBand(j) = vgroup("[%j]Band",fi.peak_eq(l,f,b))
with {
a = j+1; // just so that band numbers don't start at 0
l = vslider("[2]Level[unit:db]",0,-70,12,0.01) : si.smoo;
f = nentry("[1]Freq",(80+(1000*8/N*(j+1)-80)),20,20000,0.01) : si.smoo;
b = f/hslider("[0]Q[style:knob]",1,1,50,0.01) : si.smoo;
};
};
process = filterBank(nBands);
gives the ERROR : path '/Filter_Bank/Band/Q' is already used message. This is because we typically want to distinguish pathnames to be controlled by OSC messages for instance. So a proper solution would be to rewrite the code with:
import("stdfaust.lib");
nBands = 8;
filterBank(N) = hgroup("Filter Bank",seq(i,N,oneBand(i)))
with {
oneBand(j) = vgroup("[%j]Band %a",fi.peak_eq(l,f,b))
with {
a = j+1; // just so that band numbers don't start at 0
l = vslider("[2]Level[unit:db]",0,-70,12,0.01) : si.smoo;
f = nentry("[1]Freq",(80+(1000*8/N*(j+1)-80)),20,20000,0.01) : si.smoo;
b = f/hslider("[0]Q[style:knob]",1,1,50,0.01) : si.smoo;
};
};
process = filterBank(nBands);
Mathematical functions out of domain errors
Error messages will be produced when the mathematical functions are used outside of their domain, and if the problematic computation is done at compile time. If the out of domain computation may be done at runtime, then a warning can be produced using the -me option (see Warning messages section).
Modulo primitive error
The modulo % assumes that the denominator is not 0, thus the following code:
process = _%0;
will produce the following error:
ERROR : % by 0 in IN[0] % 0
The same kind of errors will be produced for acos, asin, fmod, log10, log, remainder and sqrt functions.
FIR and backends related errors
Some primitives of the language are not available in some backends.
The example code:
fun = ffunction(float fun(float), <fun.h>, "");
process = fun;
compiled with the wast/wasm backends using: faust -lang wast errors.dsp will produce the following error:
ERROR : calling foreign function 'fun' is not allowed in this compilation mode
and the same kind of errors would happen also with foreign variables or constants.
[TO COMPLETE]
Compiler option errors
All compiler options cannot be used with all backends. Moreover, some compiler options can not be combined. These will typically trigger errors, before any compilation actually begins.
[TO COMPLETE]
Warning messages
Warning messages do not stop the compilation process, but allow to get usefull informations on potential problematic code. The messages can be printed using the -wall compilation option. Mathematical out-of-domain error warning messages are displayed when both -wall and -me options are used.