A. 语法规范
这里是简化的凹语言语法规范,主要是作为理解参考。
A.1 文件结构
凹语言是一个精心设计的语言,语法非常利于理解和解析。一个凹语言文件中,顶级的语法元素只有5种:import、type、const、var以及fn。每个文件的语法规范定义如下:
SourceFile = { ImportDecl ";" } { TopLevelDecl ";" } .
TopLevelDecl = Declaration | FuncDecl | MethodDecl .
Declaration = ConstDecl | TypeDecl | GlobalDecl .
SourceFile表示一个凹源文件,由以下两个部分组成:ImportDec(导入声明)和TopLevelDecl(顶级声明)。其中TopLevelDecl由通用声明、函数声明和方法声明组成,通用声明再分为常量、类型和变量声明。
导入语法如下:
ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec = ImportPath [ "=>" PackageName ] .
ImportPath = string_lit .
PackageName = identifier .
import 关键字用于导入包,导入的包还可以被重新命名(对应PackageName)。
以下代码是一个凹源文件的对应例子:
// 凹语言例子
import ("a", "b")
type SomeType int
const PI = 3.14
global Length = 1 // 全局变量
func main {
sum: int // 局部变量
println(sum)
}
只要通过每行开头的不同关键字就可以明确属于那种声明类型。
A.2 函数和方法
函数是所有编程语言中的核心,因为只有函数的语句才有了计算的功能。凹语言的函数也是一种值数据,可以定义包级别的函数,也可以为自定义的类型定义方法,同时还可以在局部作用域内定义闭包函数。在顶级声明中包含函数和方法的声明,从语法角度看函数是没有接收者参数的方法特例。
函数的语法规则如下:
FuncDecl = "func" MethodName [ Signature ] [ FnBody ] .
MethodDecl = "func" Receiver "." MethodName [ Signature ] [ FnBody ] .
MethodName = identifier .
Receiver = identifier .
Signature = Parameters [ "=>" Result ] .
Result = Parameters | ":" Type .
Parameters = "(" [ ParameterList [ "," ] ] ")" .
ParameterList = ParameterDecl { "," ParameterDecl } .
ParameterDecl = [ IdentifierList ] ":" [ "..." ] Type .
其中FnDecl表示函数,而MethodDecl表示方法。MethodDecl表示的方法规范比函数多了Receiver语法结构,Receiver表示方法的接收者参数。然后是MethodName表示的函数或方法名,Signature表示函数的签名(或者叫类型),最后是函数的主体。需要注意的是函数的签名只有输入参数和返回值部分,因此函数或方法的名字、以及方法的接收者类型都不是函数签名的组成部分。从以上定义还可以发现,Receiver、Parameters和Result都是ParameterList定义,因此有着相同的语法结构(在语法树中也是有着相同的结构)。
下面是函数和方法的常见形式:
# 函数声明
func()
func(x :int) => int
func(a, _ :int, z :f32) => bool
func(a, b :int, z :f32) => (bool)
func(prefix :string, values :...int)
func(a, b :int, z :f64, opt :...any) => (success bool)
func(int, int, f64) => (f64, *[]int)
func(n :int) => func(p :*T)
# 方法定义
func Person.GetName() => string { return this.name }
A.3 关键字和运算符
关键字是语法的组成元素,不能用于标识符。凹语言目前有19个关键字:
break defer if return
case else import struct
const for interface switch
continue func map type
default global range
以下是凹语言的运算符和与标点:
+ & += &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^= =>
运算符用于组成表达式,标点用于组成或分隔语句。
A.4 数据类型
除了布尔类型、字符、整数、浮点数、字符串等基础类型,凹语言还提供了指针、数组、切片、结构体、map、函数和接口等复合类型。复合类型的语法定义如下:
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .
AliasDecl = identifier "=" Type .
TypeDef = identifier ":" Type .
Type = TypeName | TypeLit | "(" Type ")" .
TypeName = identifier | PackageName "." identifier .
TypeLit = PointerType | ArrayType | SliceType
| StructType | MapType | FnType | InterfaceType
.
其中TypeDecl定义了类型声明的语法规范,可以是每个类型独立定义或通过小括弧包含按组定义。其中AliasDecl是定义类型的别名(名字和类型中间有个赋值符号),而TypeDef则是定义一个新的类型。而基础的Type就是由标识符或者是小括弧包含的其它类型表示。TypeName不仅仅可以从当前空间的标识符定义新类型,还支持从其它包导入的标识符定义类型。而TypeLit表示类型面值,比如基于已有类型的指针,或者是匿名的结构体都属于类型的面值。
下面以结构体为例展示复合类型的语法:
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList ":" Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag = string_lit .
IdentifierList = identifier { "," identifier } .
TypeName = identifier | PackageName "." identifier .
结构体通过struct关键字开始定义,然后在大括弧中包含成员的定义。每一个FieldDecl表示一组有着相同类型和Tag字符串的标识符名字,或者是嵌入的匿名类型或类型指针。
以下是结构体的例子:
type MyStruct struct {
a, b : int "int value"
string
}
其中a和b成员不仅仅有着相同的int类型,同时还有着相同的Tag字符串,最后的成员是嵌入一个匿名的字符串。
A.5 语句块和语句
语句近似看作是函数体内可独立执行的代码,语句块是由大括弧定义的语句容器,语句块和语句只能在函数体内部定义。语句块和语句是在函数体部分定义,函数体就是一个语句块。语句块的语法规范如下:
FnBody = Block .
Block = "{" StatementList "}" .
StatementList = { Statement ";" } .
Statement = Declaration | ExpressionStmt
| IfStmt | Block | ReturnStmt
| ForStmt | BreakStmt | ContinueStmt
.
FnBody函数体对应一个Block语句块。每个Block语句块内部由多个语句列表StatementList组成,每个语句之间通过分号分隔。语句又可分为声明语句、标签语句、普通表达式语句和其它诸多控制流语句。需要注意的是,Block语句块也是一种合法的语句,因此函数体实际上是又Block组成的多叉树结构表示,每个Block结点又可以递归保存其他的可嵌套Block的控制流等语句。
其中声明语句和表达式语句语法如下(声明语句的细节可参考语言文档,这里不做展开):
Declaration = ConstDecl | TypeDecl | GlobalDecl .
TopLevelDecl = Declaration | FuncDecl | MethodDecl .
ExpressionStmt = Expression .
下面是声明语句和表达式语句的例子:
const Pi = 3.14
type MyInt int32
global x = 123 // 全局变量
x = x + 1
if 和 return 语句的语法定义如下:
IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
ReturnStmt = "return" [ ExpressionList ] .
ExpressionList = Expression { "," Expression } .
if 和 return 语句的例子如下:
func main {
if 1+1 == 2 { return }
}
for 循环语句的语法定义如下:
ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .
ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .
RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
循环的例子如下:
for {} # 死循环
for true {} # 死循环
for ; true ; {} # 死循环
# C 语言风格循环
for i := 0; i < 10; i++ {}
# 循环迭代列表的元素
list := make([]int, 10)
for i, v := range list {}
基于声明、定义、分支、循环这些基本的语句,就可以构造出任意复杂的程序。