Attributes and Directives
For the normative rules, including preprocessing and source dependencies, see Attributes, Directives, and Interoperability.
DQ has declaration attributes written with [[...]] and source directives
written with #.
Attributes
Attributes can appear before a declaration or after the declaration item, depending on the declaration kind.
[[external]] function printf(fmt : ^char, ...) -> int
function printf(fmt : ^char, ...) -> int [[external('printf')]]
Multiple attributes can be written in one list.
function Run() [[virtual, abstract]]
Common Attributes
| Attribute | Use |
|---|---|
[[external]] |
Link a function or global variable to an external symbol |
[[external('name')]] |
Link to a specific external symbol name |
[[overload]] |
Mark a function, method, or constructor as overloaded |
[[virtual]] |
Mark an object method as virtual |
[[override]] |
Mark an object method as overriding a base virtual method |
[[abstract]] |
Mark a virtual method as abstract |
[[final]] |
Mark a method or virtual slot as final |
[[packed]] |
Request packed aggregate layout |
[[align(n)]] |
Request alignment |
[[volatile]] |
Mark low-level volatile storage/access where supported |
[[noread]] |
Forbid direct reads from low-level storage |
[[nowrite]] |
Forbid direct writes to low-level storage |
[[regrw]], [[regro]], [[regwo]] |
Declare volatile read/write, read-only, or write-only registers |
[[cexport]] |
Export a symbol using C-compatible linkage where supported |
[[weak]] |
Emit a module-level function definition with weak linker binding |
[[nowarn]] |
Suppress warnings for the declaration where supported |
Unsupported or inapplicable attributes may be ignored with a compiler warning.
Volatile and Register Access
The register attributes provide concise declarations for memory-mapped I/O:
struct SDeviceRegisters:
STATUS : [[regro]] uint32
CONTROL : [[regrw]] uint32
COMMAND : [[regwo]] uint32
endstruct
They expand to the fundamental access attributes:
| Register attribute | Equivalent attributes | Allowed operations |
|---|---|---|
[[regrw]] |
[[volatile]] |
read and write |
[[regro]] |
[[volatile, nowrite]] |
read only |
[[regwo]] |
[[volatile, noread]] |
write only |
Reading [[noread]] storage or writing [[nowrite]] storage is a compile error.
A modify-assignment such as |= reads and writes its target and therefore needs
both operations to be allowed. noread and nowrite cannot be combined on the
same declaration.
Volatile accesses are observable compiler operations, but they are not atomic
and do not imply a CPU or compiler memory barrier. Taking an address or binding
a reference currently discards noread and nowrite; this is an explicit
low-level escape. Whole-structure initialization and copying also do not yet
apply the access restrictions of individual fields.
External Attribute
[[external]] without an argument uses the DQ declaration name as the external
symbol name.
[[external]] function puts(s : ^char) -> int
Use an argument when the external symbol name differs.
[[external('fprintf')]] function c_fprintf(stream : pointer, fmt : ^char, ...) -> int
External global variables are supported.
var libc_stdout : pointer [[external('stdout')]]
Weak Attribute
[[weak]] gives a module-level function definition weak linker binding, allowing
a strong definition with the same linker name to replace it. It does not change
the function's linker name. Combine it with [[cexport]] when C or assembly code
must override the function by its unmangled DQ name.
function DefaultHandler() [[weak, cexport]]:
// fallback implementation
endfunc
The attribute does not take arguments and is not valid on external declarations or object and struct methods.
Preprocessor Symbols
#define defines a preprocessor symbol.
#define DEBUG
#define BUFFER_SIZE = 4096
Preprocessor symbols can be read through @def.
var size : int = @def.BUFFER_SIZE
DQ does not provide C-style textual macro expansion.
Conditional Compilation
Conditional directives include:
#if CONDITION
#ifdef SYMBOL
#ifndef SYMBOL
#elif CONDITION
#elifdef SYMBOL
#elifndef SYMBOL
#else
#endif
Example:
#ifdef DEBUG
const LOGGING : bool = true
#else
const LOGGING : bool = false
#endif
Directive blocks can also be written inline with #{...}.
var value : int = #{ifdef FAST} 1 #{else} 2 #{endif}
#if and #elif expressions resolve unqualified names in the surrounding DQ
lexical scope. The result must be a compile-time constant Boolean value, and
only declarations already visible at the directive can be used. Preprocessor
definitions are a separate namespace and are referenced explicitly:
const API_LEVEL : int = 4
#define TARGET_VERSION = 7
#if API_LEVEL >= 3
const HAS_NEW_API : bool = true
#endif
#if @def.TARGET_VERSION >= 7
const HAS_NEW_TARGET : bool = true
#endif
#ifdef, #ifndef, #elifdef, and #elifndef test preprocessor definitions
directly when given a bare name. They can also test a value symbol in any named
scope, for example #ifdef @.FEATURE or #ifdef @module.FEATURE. Bare names in
#define value expressions continue to refer to earlier preprocessor definitions.
Include
Source include directives are supported.
#include "file.dqi"
#include 'file.dqi'
Include files are processed by the source feeder before parsing the resulting DQ source.
A module can keep its public declarations in a same-basename .dqh file:
#include header
For file.dq, this includes file.dqh. The directive is valid only before
implementation and cannot be used from a .dqh file.
Bare include paths first resolve relative to the current source file and then
through package search roots. ./ and ../ are source-file relative, while
^/ is module-root relative.
Implementation includes that must invalidate the module object can be declared
before implementation without including their contents there:
#srcdep "somefunc_impl.dqi"
#srcdep uses the same path resolution as #include. It records only the named
file and does not parse it or discover further dependencies. Automatic include
dependencies are limited to directives written directly in the main .dq or its
same-basename .dqh; includes nested inside other .dqi files are still parsed
but are not tracked automatically.
Link Libraries
#linklib requests linking with an external library.
#linklib('z')
function zlibVersion() -> ^char [[external]]
The exact linker behavior depends on the target platform and compiler driver.
Directive Style
Directives start with # and are part of the DQ source feeder layer. They are
not general-purpose textual macros. Prefer DQ constants, functions, and modules
for normal program structure.