Directives - IS4Code/Sona GitHub Wiki
Directives are pieces of extra syntax placed either alone or attached to other elements, which affect the interpretation of the code in a specific way, depending on the kind of the directive used.
The syntax of a directive consists of the #
character, followed immediately by the name of the directive and whitespace. If this collides with the usage of the #
operator, using whitespace or parentheses after it fixes it.
Inside a directive, the normal parsing rules do not apply:
- Whitespace is significant and may affect the result of the directive.
- The directive is terminated by a newline or another
#
. To prevent it from terminating by a line end, it can be escaped with\
. - Some directives allow arbitrary expressions, in which case parser rules may also extend them across multiple lines.
- Some standard keywords may not be recognized, or other directive-specific keywords may be introduced.
Note that while the directive syntax resembles that used by the preprocessor in other languages, there is no separate preprocessing phase ‒ some information is expressible only through a directive, and the parser reacts to it directly.
Attributes offer means to attach information to code existing code elements. They come in two forms: local, starting with #:
, #item
, #type
, #method
, #property
, #return
, #param
, #field
, #event
, or #constructor
, followed immediately by a code element, and global, using #program
, #entry
, #assembly
, or #module
, usable as a top-level statement.
Inside the attribute syntax, using any sorts of parentheses ((…)
, […]
, {…}
) reverts back to the usual parsing rules within the parentheses, allowing newlines and insignificant whitespace as if outside the attribute. Additionally, a line ending with a comma (,
) extends the directive to the next line.
Inline F# source can be embedded using #inline
. It can be used as a stand-alone statement, expression, type, or pattern, to insert a piece of F# code in its place, up until #endinline
.
Pragmas serve to configure the compiler to output slightly different code from parts of its syntax. A pragma directive starts with #pragma
, followed by name of the specific pragma and its parameters. Other than affecting the code that follows it, a pragma has no effect on the point of code where it is used itself, basically equivalent to whitespace.
pragma:
'#pragma' pragma_name pragma_args;
Each individual pragma type has its own state stack, with the top state being the one that currently affects the code. This stack can be manipulated using #pragma push
and #pragma pop
; using the pragma without these directives always affects the top state.
push_pragma:
'#pragma push' name args;
#pragma push
is used to push a new state on top of the stack of the pragma identified by name
. Its effect is the same as using #pragma name args
, but can be reverted using #pragma pop name
.
Example:
#pragma push echo printf
echo "Test"
#pragma pop echo
echo "Test"
This configures the echo
pragma to use printf
for the next line, and restores it afterwards to the default (printfn
).
push_pragma:
'#pragma pop' name;
#pragma pop
is used to remove the last pushed state of name
and restore the previous state. It is an error if used without the corresponding prior #pragma push
.
echo_fragma:
'#pragma echo' name;
#pragma echo
configures the name of the function used by the subsequent echo
statements, such as printf
, printfn
, eprintf
, eprintfn
.
newline_pragma:
'#pragma newline' string;
#pragma newline
sets the newline sequence used when outputting non-verbatim strings spanning multiple lines. It takes a string argument, such as "\n"
or "\r\n"
.
type_kind_pragma:
'#pragma' ('tuple' | 'record' | 'option') ('class' | 'struct');
These three pragmas are used to affect what kind of type is used by default for tuples, records, and options, taking a struct
or class
argument to indicate whether these types should have value or reference semantics.