词法约定#

本页面规定 MoonBit 的词法形式。运行时表示、API 和字面量重载见基础

MoonBit 源文本必须是格式正确的 UTF-8。格式错误的输入会报告词法错误。在每个位置,词法分析器都会取最长的有效词法单元。空白会被丢弃,而换行参与自动分号插入。

在产生式中,{ symbol } 表示重复零次或多次,{ symbol }+ 表示重复一次或多次,[ symbol ] 表示出现零次或一次,x...y 表示包含两端的范围。圆括号用于组合形式。except 子句从其左侧的完整形式中排除所列形式。

通用词法类#

unicode-scalar-value ::= U+0000...U+D7FF | U+E000...U+10FFFF

newline-character ::= LF | CR | U+2028 | U+2029

newline ::= newline-character | CR LF

whitespace ::= U+0009 | U+000B | U+000C | U+0020 | U+00A0 | U+1680
             | U+2000...U+200A | U+202F | U+205F | U+3000 | U+FEFF

line-character ::= unicode-scalar-value except newline-character

字符串字面量#

string-literal ::= " { string-character } "

string-character ::= regular-string-character
                   | simple-escape-sequence
                   | unicode-escape-sequence
                   | interpolation

regular-string-character ::= unicode-scalar-value
                             except ", \, and newline-character

simple-escape-sequence ::= \ (\ | " | ' | n | t | b | r | f | /)

unicode-escape-sequence ::= \u (0...9 | A...F | a...f) (0...9 | A...F | a...f)
                            (0...9 | A...F | a...f) (0...9 | A...F | a...f)
                          | \u{ { 0...9 | A...F | a...f }+ }

简单转义序列的含义如下:

序列

字符

\\

反斜杠(U+005C)

\"

双引号(U+0022)

\'

单引号(U+0027)

\/

正斜杠(U+002F)

\n

换行符(U+000A)

\r

回车符(U+000D)

\t

水平制表符(U+0009)

\b

退格符(U+0008)

\f

换页符(U+000C)

Unicode 转义必须表示 Unicode 标量值。若在右引号之前遇到换行,则报告未终止的字符串字面量。

插值#

interpolation ::= \{ { whitespace } expression { whitespace } }

表达式不能为空,且必须在与之匹配的 } 处结束。嵌套字面量中的大括号不影响匹配。嵌套插值会被递归识别。不允许出现换行、// 注释、属性和多行字符串字面量。

多行字符串字面量#

multiline-string-literal ::= multiline-string-line
                           { newline multiline-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-character | interpolation }

multiline-regular-character ::= unicode-scalar-value
                                except newline-character

结果中省略各行前缀,并使用 U+000A 连接各行。最后一个带前缀的空行会添加末尾换行符。#| 行是原样文本。在 $| 行中,只有 \{ 会开始插值。插值表达式内不允许多行字符串。

字节序列字面量#

bytes-literal ::= b" { bytes-character } "

bytes-character ::= regular-string-character
                  | simple-escape-sequence
                  | byte-escape-sequence
                  | interpolation

byte-escape-sequence ::= \x (0...9 | A...F | a...f) (0...9 | A...F | a...f)
                       | \o (0...3) (0...7) (0...7)

若在右引号之前遇到换行,则报告未终止的字面量。非 ASCII 源字符写入其 UTF-8 编码。\xHH\oDDD 各写入一个取值为 0 到 255 的字节。插值遵循字符串字面量规则。字节序列字面量没有多行形式。

正则字面量#

regex-literal ::= re" { regex-character } "

regex-character ::= regular-string-character
                  | \ (unicode-scalar-value except { and newline-character)
                  | interpolation

反斜杠会原样保留给正则解析器,而 \{ 会开始插值。带插值的正则字面量仅在 lex-pattern 上下文中接受。若在右引号之前遇到换行,则报告未终止的字面量。参见正则字面量表达式

字符字面量#

character-literal ::= ' regular-character '
                    | ' character-escape-sequence '

regular-character ::= unicode-scalar-value
                      except ', \, and newline-character

character-escape-sequence ::= simple-escape-sequence
                            | unicode-escape-sequence

字符字面量恰好包含一个 Unicode 标量值或转义序列。

字节字面量#

byte-literal ::= b' regular-byte-character '
               | b' byte-character-escape-sequence '

regular-byte-character ::= (U+0000...U+007F)
                           except ', \, and newline-character

byte-character-escape-sequence ::= simple-escape-sequence
                                 | byte-escape-sequence

未转义的字节必须是 ASCII。字节字面量中不允许 Unicode 转义。

注释#

doc-comment ::= /// { line-character }

line-comment ::= // { line-character }

当两者都能匹配时,doc-comment 优先于 line-comment。MoonBit 不支持块注释。

标识符#

non-ascii ::= U+00A1...U+00AC
              | U+00AE...U+02AF
              | U+1100...U+11FF
              | U+1E00...U+1EFF
              | U+2070...U+209F
              | U+2150...U+218F
              | U+2E80...U+2EFF
              | U+2FF0...U+2FFF
              | U+3001...U+30FF
              | U+31C0...U+9FFF
              | U+AC00...U+D7FF
              | U+F900...U+FAFF
              | U+FE00...U+FE0F
              | U+FE30...U+FE4F
              | U+1F000...U+1FBFF
              | U+20000...U+2A6DF
              | U+2A700...U+2EBEF
              | U+2F800...U+2FA1F
              | U+30000...U+323AF
              | U+E0100...U+E01EF

uident ::= (A...Z) { A...Z | a...z | 0...9 | _ | non-ascii }

lident ::= _ { A...Z | a...z | 0...9 | _ | non-ascii }+
         | (a...z | non-ascii) { A...Z | a...z | 0...9 | _ | non-ascii }

underscore ::= _

以上字符范围按原样使用,不进行 Unicode 规范化。uident 以 ASCII 大写字母开头。其他标识符拼写以 ASCII 小写字母、_non-ascii 中的字符开头。单独的 _ 是专用词法单元。关键字也使用各自的专用词法单元。以 ASCII 十进制数字开头时会开始一个数字字面量。

关键字#

keyword ::= as | else | extern | fn | if | let
          | const | match | using | mut | type
          | struct | enum | extenum | trait
          | derive | while | break | continue | import | return
          | throw | raise | try | catch | pub | priv
          | proof_assert | proof_let | readonly | true | false
          | test | loop | for | in | impl | with
          | guard | async | is | suberror | and | letrec
          | enumview | noraise | defer | lexscan
          | where | declare | nobreak | extend | try! | guard!

关键字优先于标识符。

以下拼写为保留字。尚未成为关键字的保留字会被词法分析为标识符或标签,并报告保留关键字警告。

reserved-word ::= module | move | ref | static | super | unsafe
                | use | await | dyn | abstract | do | final
                | macro | override | typeof | virtual | yield
                | local | method | alias | assert | package
                | recur | isnot | define
                | downcast | inherit | member | namespace | upcast
                | void | lazy | include | mixin | protected
                | sealed | constructor | atomic | volatile
                | anyframe | anytype | asm | comptime | errdefer
                | export | opaque | orelse | resume | threadlocal
                | unreachable | dynclass | dynobj | dynrec | var
                | finally | noasync | assume

标签#

label ::= _ { A...Z | a...z | 0...9 | _ | non-ascii }+ ~
        | ((a...z | non-ascii) { A...Z | a...z | 0...9 | _ | non-ascii } ~) except keyword~

~ 必须紧跟名称。以 ASCII 大写字母开头的标识符和关键字不能构成标签。

包名#

package-part ::= (A...Z | a...z | _) { A...Z | a...z | 0...9 | _ | - }

package-name ::= @ package-part { / package-part }

包名仅限 ASCII。连字符不能位于包名部分的开头。前导 @、斜杠和各个部分必须彼此相邻。

属性#

attribute-name ::= (A...Z | a...z | _) { A...Z | a...z | 0...9 | _ }

attribute ::= # attribute-name [ . attribute-name ] { line-character }

可选的点号限定名之后,到下一个换行为止的所有内容都是原始载荷。interpolation 中不允许使用属性。载荷文法见属性

数字字面量#

integer-nums ::= (0...9) { 0...9 | _ }
               | 0 (x | X) (0...9 | A...F | a...f) { 0...9 | A...F | a...f | _ }
               | 0 (o | O) (0...7) { 0...7 | _ }
               | 0 (b | B) (0 | 1) { 0 | 1 | _ }

integer-literal ::= integer-nums [ UL | U | L | N ]

double-dec ::= (0...9) { 0...9 | _ } . { 0...9 | _ }
               [ (e | E) [ + | - ] (0...9) { 0...9 | _ } ]

double-hex ::= 0 (x | X) (0...9 | A...F | a...f)
               { 0...9 | A...F | a...f | _ } .
               { 0...9 | A...F | a...f | _ }
               [ (p | P) [ + | - ] (0...9) { 0...9 | _ } ]

double-literal ::= double-dec | double-hex

float-dec ::= double-dec F

float-hex ::= 0 (x | X) (0...9 | A...F | a...f)
              { 0...9 | A...F | a...f | _ } .
              { 0...9 | A...F | a...f | _ }
              (p | P) [ + | - ] (0...9) { 0...9 | _ } F

float-literal ::= float-dec | float-hex

数的第一个数字之后,下划线可以重复出现,也可以出现在末尾。大写后缀分别指定 UIntU)、Int64L)、UInt64UL)、BigIntN)或 FloatF)。不带后缀的浮点字面量为 Double

浮点字面量始终包含小数点。十六进制 Float 必须在 F 前包含由 pP 引导的指数部分,因此 0x1.FDouble。正负号是独立的词法单元。紧接 .. 时,整数词法单元先结束,因此 1..=21..= 开始。

1.1.F 也分别是合法的 DoubleFloat 字面量。

点号前缀词法单元#

tuple-accessor ::= . { 0...9 }+

dot-identifier ::= . (A...Z | a...z | _ | non-ascii) { A...Z | a...z | 0...9 | _ | non-ascii }

这些形式中不能包含空白。元组访问器中不能有下划线。点号标识符遵循标识符的大小写规则,但不查询关键字表,因此 .if 合法。单独的 . 会报告词法错误。

运算符与分隔符#

delimiter ::= ( | ) | , | :: | : | ;
            | [ | ] | { | } | [| | |]

operator ::= => | ->
           | && | & | ^
           | * | / | %
           | << | >>
           | = | > | <| | <? | <+ | <
           | == | != | =~ | <= | >=
           | | | || | + | - | ? | !
           | += | -= | *= | /= | %= | |>
           | .. | ..= | ..< | ..<= | >.. | >=.. | ...

列出的词法单元中,最长者优先。

自动分号插入#

换行后,如果前一个词法单元可以结束语句,且后一个词法单元可以开始语句,词法分析器可能会插入 ;。文件末尾也可作为有效的后继。词法分析器不会在 } 前插入分号,也不会在多行字符串的相邻行之间插入分号。

.mbti 扩展#

.mbti 的词法文法在 .mbt 的基础上增加以下关键字:

mbti-keyword ::= keyword | package

.mbti 中,package 的匹配优先级高于标识符,且不能构成标签。在 .mbt 中,它遵循上文所述的保留字规则,并可构成 package~