Table of Contents

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 disable directive affects diagnostics on that line and subsequent lines.
  • A matching restore re-enables the specified diagnostic IDs (or all IDs when no IDs are provided).
  • disable-next-line suppresses 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-unit members.
  • 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.