Lexical Conventions#
This page specifies MoonBit lexical forms. Runtime representation, APIs, and literal overloading are covered in Fundamentals.
MoonBit source text must be well-formed UTF-8. Malformed input reports a lexical error. At each position, the lexer consumes the longest valid token. Whitespace is discarded, while newlines participate in automatic semicolon insertion.
In the productions, { symbol } means zero or more repetitions,
{ symbol }+ means one or more repetitions, [ symbol ] means zero or one
occurrence, and x...y denotes an inclusive range. Parentheses group a form.
An except clause removes the listed forms from the complete form on its left.
Common Lexical Classes#
unicode-scalar-value ::= U+0000U+D7FFU+E000U+10FFFF newline-character ::= LFCRU+2028U+2029 newline ::= newline-characterCRLF whitespace ::= U+0009U+000BU+000CU+0020U+00A0U+1680 U+2000U+200AU+202FU+205FU+3000U+FEFF line-character ::= unicode-scalar-valuenewline-character
String Literals#
string-literal ::= "string-character" string-character ::= regular-string-character simple-escape-sequence unicode-escape-sequence interpolation regular-string-character ::= unicode-scalar-value "\newline-character simple-escape-sequence ::= \\"'ntbrf/ unicode-escape-sequence ::= \u09AFaf09AFaf 09AFaf09AFaf \u{09AFaf}
The simple escape sequences have the following meanings:
Sequence |
Character |
|---|---|
|
Backslash (U+005C) |
|
Double quote (U+0022) |
|
Single quote (U+0027) |
|
Forward slash (U+002F) |
|
Line feed (U+000A) |
|
Carriage return (U+000D) |
|
Horizontal tab (U+0009) |
|
Backspace (U+0008) |
|
Form feed (U+000C) |
A Unicode escape must denote a Unicode scalar value. A newline before the closing quote reports an unterminated string literal.
Interpolation#
interpolation ::= \{whitespaceexpressionwhitespace}
The expression must be nonempty and end at the matching }. Braces inside
nested literals do not affect matching. Nested interpolations are recognized
recursively. Newlines, // comments, attributes, and multiline string literals
are not permitted.
Multiline String Literals#
multiline-string-literal ::= multiline-string-line newlinemultiline-string-line multiline-string-line ::= raw-multiline-string-line interpolated-multiline-string-line raw-multiline-string-line ::= #|multiline-regular-character interpolated-multiline-string-line ::= $|multiline-regular-characterinterpolation multiline-regular-character ::= unicode-scalar-value newline-character
The prefixes are omitted from the result, and lines are joined with U+000A. A
final empty prefixed line adds a trailing line feed. A #| line is literal. In
a $| line, only \{ begins interpolation. Multiline strings are not permitted
inside interpolation expressions.
Bytes Literals#
bytes-literal ::= b"bytes-character" bytes-character ::= regular-string-character simple-escape-sequence byte-escape-sequence interpolation byte-escape-sequence ::= \x09AFaf09AFaf \o030707
A newline before the closing quote reports an unterminated literal. Non-ASCII
source characters contribute their UTF-8 encoding. \xHH and \oDDD each
contribute one byte with a value from 0 to 255. Interpolation follows the
string-literal rules. There is no multiline bytes-literal form.
Regex Literals#
regex-literal ::= re"regex-character" regex-character ::= regular-string-character \unicode-scalar-value{newline-character interpolation
Backslashes are preserved for the regex parser, while \{ starts an
interpolation. Interpolated regex literals are accepted only in lex-pattern
contexts. A newline before the closing quote reports an unterminated literal.
See Regex Literal Expression.
Character Literals#
character-literal ::= 'regular-character' 'character-escape-sequence' regular-character ::= unicode-scalar-value '\newline-character character-escape-sequence ::= simple-escape-sequence unicode-escape-sequence
A character literal contains exactly one Unicode scalar value or escape sequence.
Byte Literals#
byte-literal ::= b'regular-byte-character' b'byte-character-escape-sequence' regular-byte-character ::= U+0000U+007F '\newline-character byte-character-escape-sequence ::= simple-escape-sequence byte-escape-sequence
An unescaped byte is ASCII. Unicode escapes are invalid in byte literals.
Identifiers#
non-ascii ::= U+00A1U+00AC U+00AEU+02AF U+1100U+11FF U+1E00U+1EFF U+2070U+209F U+2150U+218F U+2E80U+2EFF U+2FF0U+2FFF U+3001U+30FF U+31C0U+9FFF U+AC00U+D7FF U+F900U+FAFF U+FE00U+FE0F U+FE30U+FE4F U+1F000U+1FBFF U+20000U+2A6DF U+2A700U+2EBEF U+2F800U+2FA1F U+30000U+323AF U+E0100U+E01EF uident ::= AZAZaz09_non-ascii lident ::= _AZaz09_non-ascii aznon-asciiAZaz09_non-ascii underscore ::= _
These exact ranges are used, without Unicode normalization. A uident begins
with an ASCII uppercase letter. All other identifier spellings begin with an
ASCII lowercase letter, _, or a character in non-ascii. The exact spelling
_ is a dedicated token. Keywords also use their own tokens. A leading ASCII
decimal digit starts a numeric literal.
Keywords#
keyword ::= aselseexternfniflet constmatchusingmuttype structenumextenumtrait derivewhilebreakcontinueimportreturn throwraisetrycatchpubpriv proof_assertproof_letreadonlytruefalse testloopforinimplwith guardasyncissuberrorandletrec enumviewnoraisenocanceldeferlexscan wheredeclarenobreakextendtry!guard!
Keywords take precedence over identifiers.
The following spellings are reserved. A spelling that is not already a keyword is otherwise tokenized as an identifier or label and reports a reserved-keyword warning.
reserved-word ::= modulemoverefstaticsuperunsafe useawaitdynabstractdofinal macrooverridetypeofvirtualyield localmethodaliasassertpackage recurisnotdefine downcastinheritmembernamespaceupcast voidlazyincludemixinprotected sealedconstructoratomicvolatile anyframeanytypeasmcomptimeerrdefer exportopaqueorelseresumethreadlocal unreachabledynclassdynobjdynrecvar finallynoasyncassume
Labels#
label ::= _AZaz09_non-ascii~ aznon-asciiAZaz09_non-ascii~keyword~
The ~ must immediately follow the name. ASCII-uppercase identifiers and
keywords cannot form labels.
Package Names#
package-part ::= AZaz_AZaz09_- package-name ::= @package-part/package-part
Package names are ASCII-only. A hyphen cannot begin a package part. The leading
@, slashes, and parts must be adjacent.
Attributes#
attribute-name ::= AZaz_AZaz09_ attribute ::= #attribute-name.attribute-nameline-character
After the optional dot-qualified name, everything through the next newline is
the raw payload. Attributes are not permitted inside an interpolation. See
Attribute for the payload grammar.
Numeric Literals#
integer-nums ::= 0909_ 0xX09AFaf09AFaf_ 0oO0707_ 0bB0101_ integer-literal ::= integer-numsULULN double-dec ::= 0909_.09_ eE+-0909_ double-hex ::= 0xX09AFaf 09AFaf_. 09AFaf_ pP+-0909_ double-literal ::= double-decdouble-hex float-dec ::= double-decF float-hex ::= 0xX09AFaf 09AFaf_. 09AFaf_ pP+-0909_F float-literal ::= float-decfloat-hex
After the first digit of a numeral, underscores may repeat or trail. Uppercase
suffixes select UInt (U), Int64 (L), UInt64 (UL), BigInt (N),
or Float (F). An unsuffixed floating-point literal is a Double.
Floating-point literals always contain a decimal point. A hexadecimal Float
requires a p or P exponent before F, so 0x1.F is a Double. Signs are
separate tokens. Before .., an integer ends first, so 1..=2 begins
with 1 and ..=.
1. and 1.F are also valid double and float literal.
Dot-Prefixed Tokens#
tuple-accessor ::= .09 dot-identifier ::= .AZaz_non-asciiAZaz09_non-ascii
These forms contain no whitespace. Tuple accessors do not permit underscores,
and dot-identifiers use the identifier case rules without consulting the
keyword table, so .if is valid. A single . reports a lexical error.
Operators and Delimiters#
delimiter ::= (),:::; []{}[||] operator ::= =>-> &&&^ */% <<>> =><|<?<+< ==!==~<=>= |||+-?! +=-=*=/=%=|> ....=..<..<=>..>=.....
The longest listed token wins. MoonBit has no generic operator-name syntax.
Automatic Semicolon Insertion#
After a newline, the lexer may insert ; when the preceding token can end a
statement and the following token can begin one. End of file is also an
eligible follower. It does not insert one before } or between adjacent
multiline string lines.
.mbti Extensions#
The .mbti lexical grammar adds this keyword to .mbt:
mbti-keyword ::= keywordpackage
In .mbti, package takes precedence over identifiers and cannot form a
label. In .mbt, it follows the reserved-word behavior above and can
form package~.
Comments#
doc-commenttakes precedence overline-commentwhen both match. MoonBit has no block-comment form.