Lexical structure
Lexical structure is the set of rules Raven uses to turn source text into names, keywords, literals, comments, and punctuation. These are the rules behind such everyday questions as which characters a name may contain, how to escape a keyword, and where a compiler directive can appear.
Source files
Raven source normally uses the .rvn file extension. The legacy .rav
extension remains recognized.
Source text may use Unicode in identifiers and comments. An executable file-based application can also begin with a Unix shebang, described under Comments and shebangs.
Identifiers
Identifiers name values, members, and types. They may begin with _, $, or
any Unicode character classified as a letter (including letter numbers such as
Roman numerals). ASCII letters therefore continue to work as before. Subsequent
characters may also include decimal digits, connector punctuation (such as
_), combining marks, and format characters. These rules mirror .NET identifier
support so Raven source can interoperate with existing APIs. Reserved keywords
cannot be used as identifiers.
Identifiers are case-sensitive. connection, Connection, and CONNECTION
name three different symbols. Keyword recognition is likewise based on the
keyword's exact spelling, so the type name Open is distinct from the lowercase
contextual keyword open.
let $ffiResult = call()
let value_1 = value0
let 数据 = call()
let сумма = total1 + total2
Keywords
Raven classifies keywords as either reserved or contextual.
| Kind | Keywords |
|---|---|
| Reserved | and, as, await, base, bool, break, byte, catch, char, class, const, continue, decimal, default, double, else, enum, false, finally, fixed, float, for, func, goto, if, int, interface, is, let, long, loop, match, new, nint, not, null, nuint, object, or, permits, return, sbyte, self, short, sizeof, stackalloc, string, struct, throw, true, try, typeof, uint, ulong, ushort, var, when, while, yield |
| Contextual | abstract, alias, explicit, final, get, implicit, import, in, init, internal, namespace, open, operator, partial, out, override, private, protected, public, ref, sealed, set, static, unit, use, val, virtual |
Reserved keywords are always treated as keywords and therefore unavailable for use as identifiers. Contextual keywords behave like ordinary
identifiers except in the syntactic positions that demand their special meaning—for example, accessibility modifiers
(public, internal, protected, private) or accessor modifiers (get, set). The partial keyword is only recognised
when declaring partial types or partial members; see Partial types and
members.
To use a reserved keyword as an identifier, prefix it with @:
class @int {}
static func @match(@return: int) -> int {
let @and = @return
return @and
}
The @ is part of the source spelling but not the logical name. For example,
the symbol declared as @match is named match in metadata and semantic
lookups. This follows the same convention as C# escaped identifiers.
Discards
The single-character _ token is reserved for discards. When a pattern,
deconstruction, or other declaration spells its designation as _ (optionally
with a type annotation), the compiler suppresses the binding and treats the
designation as a discard instead. Longer identifiers may still contain
underscores, and $ is available for interop- or DSL-oriented naming schemes.
Because _ never produces a value, using it as an expression—for example
in _ + 2—is rejected as an error.
Function expressions may also spell a parameter as _. The parameter still
consumes the corresponding delegate slot for arity and type inference, but it
does not introduce a name that can be referenced in the function body and it is
excluded from unused-parameter diagnostics.
let writeValue: (int, string) -> () = (_, value) =>
Console.WriteLine(value)
Comments and shebangs
Comments provide source-level annotations that the compiler ignores during semantic analysis. They may appear anywhere whitespace is permitted, including between tokens and at the end of a line. Comments never contribute tokens to the syntax tree; they are attached as trivia instead.
Two forms of comments are supported:
- Single-line comments start with
//and continue until the next newline or the end of the file. The terminating newline is not part of the comment. - Multi-line comments start with
/*and end with the next*/. They may span multiple lines but do not nest—a/*encountered inside a multi-line comment is treated as ordinary text. If the end of the file is reached before*/, the comment consumes the remainder of the file.
An executable file-based application may begin with a Unix shebang:
#!/usr/bin/env rvn
System.Console.WriteLine("Hello")
The #! sequence is recognized only at byte/character position zero on the
first physical line. The complete line is preserved as comment trivia and does
not participate in parsing or semantic analysis. A #! sequence elsewhere is
ordinary Raven punctuation and is diagnosed when it does not form valid
syntax. Because Unix requires #! to be the first two bytes, executable Raven
files should use UTF-8 without a byte-order mark.
Comment contents are treated as uninterpreted Unicode text. Any Unicode scalar value may appear inside a comment without escaping, including characters that would otherwise form tokens. This includes emoji and other symbols outside the Basic Multilingual Plane as long as the source file's encoding can represent them. The lexer preserves the original spelling (other than omitting the terminator), so encodings such as UTF-8 or UTF-16 must supply valid code units for the desired characters.
let answer = 42 // the ultimate answer
let greeting = "hello" // 😀 emoji and other symbols are fine
/*
Multi-line comments can document larger blocks of code.
The first */ encountered closes the comment.
*/
Diagnostic suppression
Raven supports warning pragmas that suppress diagnostics in source:
#pragma warning disable RAV0103#pragma warning disable RAV9019 RAV9012#pragma warning restore RAV0103#pragma warning disable-next-line RAV0103#pragma warning disable-next-line RAV9019 RAV9012#pragma warning disable(suppresses all diagnostics until restore)#pragma warning restore(restores all diagnostics)// pragma warning disable ...and// pragma warning restore ...are also accepted.
The directives follow these rules:
- Directives are evaluated in source order.
- A
disabledirective affects diagnostics on that line and subsequent lines. - A matching
restorere-enables the specified diagnostic IDs (or all IDs when no IDs are provided). disable-next-linesuppresses the specified diagnostic IDs (or all IDs) for only the following source line.- These directives are trivia-only; they do not introduce syntax tokens.
Conditional compilation
Raven supports compiler-integrated conditional compilation:
#if DEBUG and not PORTABLE
func mode() -> string => "debug"
#elif TRACE
func mode() -> string => "trace"
#else
func mode() -> string => "release"
#endif
The supported directives are #if, #elif, #else, and #endif. A directive
must be the first non-whitespace text on its physical source line. Conditional
groups may be nested. Each group selects at most one branch: the first #if or
#elif whose condition is true, or #else when no earlier branch was selected.
Conditions contain symbol names, the literals true and false, parentheses,
and the Raven logical operators not, and, and or. The aliases !, &&,
and || are also accepted. In precedence order, not binds most tightly,
followed by and, then or. A symbol evaluates to true when it is present in
the syntax tree's preprocessor symbol set; an undefined symbol evaluates to
false.
Project builds populate that set from the evaluated MSBuild
DefineConstants property. Direct rvnc compilation can add symbols with
--define or -define; values may be repeated or separated with commas or
semicolons.
Source in an inactive conditional branch is preserved but not parsed or type checked. It may therefore contain otherwise invalid Raven syntax without producing ordinary syntax or semantic diagnostics. Malformed conditions, misordered directives, and unterminated conditional groups are still errors.
Documentation comments
Documentation comments describe publicly consumable APIs and attach to the next declaration when only whitespace and newlines appear in between. Placing a documentation comment elsewhere produces a documentation warning and the comment is ignored for that declaration.
Two documentation spellings are supported:
- Single-line documentation comments start with
///and may be stacked. Each line contributes to the same documentation block. - Multi-line documentation comments start with
/**and end with the next*/. Leading*characters are stripped from each line to simplify indentation.
The documentation format is selected per syntax tree. Markdown is the default authoring format; XML can be requested through parser/compiler options when compatibility with XML doc tools is required. The selected format is preserved so consumers can render Markdown directly or process XML with tag awareness.
Regardless of format, the following logical sections are recognised:
summary— a short description of the declaration.param/ parameter entry — one per parameter, aligned by name.typeparam— one per type parameter, aligned by name.returns— the return value description for non-unitmembers.remarks— optional long-form notes, examples, and links.
XML documentation must be well-formed; unterminated or mismatched tags are reported as documentation warnings. Markdown content should follow CommonMark conventions, including balanced fenced code blocks. These diagnostics are suppressed when documentation mode is disabled.
Grammar
The accompanying EBNF grammar describes Raven's structural syntax. It is non-normative: contextual rules, disambiguation, and parts of the parsing process are described in the relevant feature articles instead.