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 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.
The #namespace and #package directives are necessary for scripts that define entities that are referenced from other scripts. They must be used before any other statement and define the kind of the file.
namespace_directive:
'#namespace' (name)?;
package_directive:
'#package' name;-
#namespaceindicates the code section defines contents of a given namespace (the global namespace if the name is omitted). Multiple such sections may be defined in a single file. -
#packageputs the contents of the script into a single package with the given fully-qualified name. Additional#packageor#namespacesections are not permitted.
The effect of import statements is scoped locally to these sections. Prior use of #pragma forwardref affects both these directives.
Inline source code 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.
once_pragma:
'#pragma' 'once' name args;
#pragma once can serve as a replacement for #pragma push in situations when the effect of the pragma is supposed to apply to only one code element. The program is invalid if a normal #pragma or #pragma push is used while a #pragma once for that same pragma is in use, or if the program ends and there is no code element that utilizes it.
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".
The particular value of #pragma newline is consistent across any single string literal, even if its value is changed anywhere inside of it.
type_kind_pragma:
'#pragma' ('tuple' | 'record' | 'option') ('class' | 'struct');These three pragmas are used to affect what kind of implementation 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.
type_kind_pragma:
'#pragma' 'collection' ('array' | 'list');Changes the implementation type of collection expressions and types ([]) between arrays and lists.
With #pragma collection array (the default):
- An expression
[a, b, …]creates a value of typearray<>. - The types
[]andT[]correspond toarray<>andarray<T>.
With #pragma collection list:
- An expression
[a, b, …]creates a value of typelist<>. - The types
[]andT[]correspond tolist<>andlist<T>.
recursive_pragma:
'#pragma' 'recursive' ('on' | 'true' | 'enabled' | 'off' | 'false' | 'disabled');#pragma recursive changes the semantics of all variable bindings to be able to refer to the newly bound variables from within the expressions that initialize them.
Many standard variable bindings are prohibited under #pragma recursive on the basis that such constructs are either non-sensible, confusing, or difficult to implement properly with not much value: if let/while let, use bindings, and bindings initialized with follow.
forwardref_pragma:
'#pragma' 'forwardref';#pragma forwardref enables forward referencing of elements in packages that are in its effect. This allows package-level code to refer to elements in the same package declared further in the code. This has some limitations:
- It is not possible to call forward-referenced
inlinefunctions. - Initialization of variables that depend on variables declared later may require runtime checks ensuring the variables are properly initialized before use.