By Christian Findlay

Lexical Structure

Default source is tokenized by tree-sitter-osprey; ML source uses the lexer in crates/osprey-syntax/src/ml/lexer.rs, which also inserts INDENT, DEDENT, and NEWLINE tokens. Both frontends share string unescaping and interpolation scanning.

Identifiers

Default identifiers use ASCII letters and underscores:

identifier ::= [a-zA-Z_][a-zA-Z0-9_]*

The ML lexer accepts Unicode alphabetic characters in the same leading and continuation positions, plus _; digits remain continuation-only.

Keywords

The Default grammar reserves the words used by its declarations and expressions, including let, mut, fn, extern, type, where, effect, perform, handle, in, resume, spawn, await, yield, send, recv, select, match, if, else, import, namespace, module, signature, export, opaque, state, as, true, and false.

ML reserves the corresponding words its surface uses. It deliberately does not reserve Default-only let, fn, if, or else. handler and do are reserved in ML solely so the frontend can reject the unsupported first-class handler syntax with a specific diagnostic.

in and out are variance markers inside type-parameter declarations (TYPE-VARIANCE-DECL). Outside that position, out is an identifier; in also separates a handler from its body.

Literals

Numbers

integer ::= [0-9]+
float   ::= [0-9]+ "." [0-9]+

Negative values apply unary -; the sign is not part of the token. Integer literals infer as int and decimal literals as float. Exponent notation, numeric separators, and non-decimal bases are not accepted.

Booleans and strings

Boolean literals are true and false. Strings are double-quoted. Shared unescaping recognizes \n, \r, \t, \e, \0, \", and \\; an unknown escape is preserved verbatim. Default strings may contain a source newline. The ML lexer rejects a source newline in string text, but permits one inside an interpolation expression.

${ expression } introduces interpolation in either flavor. See String Interpolation.

Lists

list ::= "[" (expression ("," expression)*)? "]"

The list elements must unify to one element type. Map and record literal forms are defined in Type System.

Operators

  • Arithmetic: +, -, *, /, %. Integer +, -, and *, integer unary -, and / and % return a checked Result; floating-point +, -, *, and unary - remain plain IEEE-754 operations (ARITH-CHECKED).
  • Comparison: ==, !=, <, >, <=, >=.
  • Boolean: &&, ||, !. && and || short-circuit.
  • Calls and data access: (), ., [].
  • Flow and matching: |>, =>, ? :, ?:.
  • Types and effects: :, ->, < >, and ! on an effect set.
  • Namespace qualification: ::.

Default uses = both to initialize a let/mut declaration and to reassign a previously declared mut binding. ML uses = for a binding and := for reassignment. A mut binding is a cell for handler-owned effect state, not a general imperative variable; where its reassignment is valid is defined in Bindings. The checker accepts reassignment only in an effect handler arm.

?: is a single token and is matched before bare ? or :. Its semantics are defined by PATTERN-RESULT-DEFAULT.

Comments

Default uses // line comments and /// documentation comments. ML accepts // line comments, nested (* ... *) block comments, and (** ... *) documentation comments.