MTProto 的正式TL声明
另请参阅TL 语言。有关组合子声明的语法,请参阅文章“TL 组合子的形式化声明”。有关模式的语法,请参阅文章“TL 模式的形式化声明”。
代币
注释与 C/C++ 中的注释相同。词法分析器会移除注释(例如,将其替换为一个空格)。空格用于分隔标记。除字符串常量外,标记不能包含空格。
角色职业:
lc-字母::= a| b| ... | z uc-字母::= A| B| ... |Z 数字::= 0| 1| ... |9 十六进制数字::=数字| a| b| c| d| e|f 下划线::=_ 字母::= lc-字母| uc-字母 标识符字符::=字母|数字|下划线
简单标识符和关键字:
lc-ident ::= lc-letter { ident-char }
uc-ident ::= uc-letter { ident-char }
namespace-ident ::= lc-ident
lc-ident-ns ::= [ namespace-ident . ] lc-ident
uc-ident-ns ::= [ namespace-ident . ] uc-ident
lc-ident-full ::= lc-ident-ns [ # hex-digit *8 ]
代币:
下划线::=_
冒号::=:
分号::=;
左括号::=(
右括号::=)
左方括号::=[
左大括号::=]
右大括号::={
三重减号::=}
自然常量:: =数字{数字} ---
lc -ident-full lc-ident uc-ident-ns等于::=井号:: =问号::=百分号::=加号:: =三角形::=三角形::=逗号::=点号:: =星号:: =排除号::=最终关键字::=新关键字::=空关键字::=
=
#
?
%
+
<
>
,
.
*
!
Final
New
Empty
Final是一个保留关键字,例如一个特殊标记。像这样的词Type不是关键字,而是具有预设值的标识符。
由一个或多个常量符号组成的标记此后应使用带引号的术语表示(例如,---代替三个减号)。
句法
TL程序的通用语法
从语法上看,TL 程序由一系列标记(标记之间用空格分隔,但在此阶段空格会被忽略)组成。一般程序结构:
TL 程序::= constr 声明{--- functions --- 函数声明| --- types --- constr 声明}
这里构造函数声明和函数声明的语法几乎完全相同(它们都是组合器):
constr-declarations ::= {声明}
fun-declarations ::= {声明}
有多种声明:
声明::=组合器声明|部分应用程序声明|最终声明
在解释如何声明组合子、偏函数和类型之前finalization,我们将介绍其他语法类别:
句法范畴和结构
表达式( expr )的概念非常重要。表达式分为类型表达式(type-expr)和数值表达式(nat-expr)。然而,它们的定义方式相同。当检查被分析表达式的类型时,即可判断其作为类型表达式或数值表达式的正确性。
type-expr ::= expr
nat-expr ::= expr
expr ::= { subexpr }
subexpr ::= term | nat-const + subexpr | subexpr + nat-const
term ::= ( expr ) | type-ident | var-ident | nat-const | % term | type-ident < expr { , expr } >
type-ident ::= boxed-type-ident | lc-ident-ns | #
boxed-type-ident ::= uc-ident-ns
var-ident ::= lc-ident | uc-ident
type-term ::= term
nat-term ::= term
E = E_1 E_2 ... E_n请注意,在表达式中写入expr意味着将函数E_1应用于参数E_2,将结果应用于E_3,依此类推。具体来说,E_1 E_2 E_3 = (E_1 E_2) E_3. 单独的类型#包含在type-ident中,因为它实际上是内置类型(#别名nat)的标识符。
该表达式E<E_1,...,E_n>是 的语法糖(E (E_1) ... (E_n)),即两个表达式都被转换成了相同的内部表示。
组合子声明
combinator-decl ::= full-combinator-id { opt-args } { args } = result-type ;
full-combinator-id ::= lc-ident-full | _
combinator-id ::= lc-ident-ns | _
opt-args ::= { var-ident { var-ident } : [ excl-mark ] type-expr }
args ::= var-ident-opt : [ conditional-def ] [ !] type-term
args ::= [ var-ident-opt : ] [ multiplicity * ] [{ args } ]
args ::= ( var-ident-opt { var-ident-opt } :[ !] type-term )
args ::= [ !] type-term
multiplicity ::= nat-term
var-ident-opt ::= var-ident | _
conditional-def ::= var-ident [ . nat-const ] ?
result-type ::= boxed-type-ident { subexpr }
result-type ::= boxed-type-ident < subexpr { , subexpr }>
有关这具体含义的描述,请参阅TL 组合子的形式化声明。这里我们只指出,在声明组合子下一个参数的类型时,只能使用同一组合子先前排列(更靠左)的参数的名称作为变量,但在声明结果类型时,可以使用其所有参数(类型为Type和#)。
请注意,以这种方式声明的组合器名称只能在 TL 本身中用作相应的裸类型。声明中出现的组合器只有内置组合器:O : #和S : # -> #。
此外,还允许使用“伪声明”,但只能用于声明内置类型(例如int ? = Int;):
内置组合器声明::=完整组合器 ID ? = 盒式类型标识 ;
部分应用(模式)
partial-app-decl ::= partial-type-app-decl | partial-comb-app-decl
partial-type-app-decl ::= boxed-type-ident subexpr { subexpr } ; | boxed-type-ident < expr { , expr } > ;
partial-comb-app-decl ::= combinator-id subexpr { subexpr };
参见TL 模式的正式声明。
类型最终确定
final-decl ::= New boxed-type-ident ; | Final boxed-type-ident ; | Empty boxed-type-ident ;
这种类型的声明意味着指定类型不能有任何构造函数:在声明之前New和声明之后都不能有构造函数Final。关键字Empty可以同时产生这两种效果。
预定义标识符
几乎所有预定义的标识符都可以使用以下模式(通常位于common.tl):
/////
//
// 常用类型
//
/////
// 内置类型
int? = Int;
long? = Long;
double? = Double;
string? = String;
// 布尔模拟
boolFalse = Bool;
boolTrue = Bool;
// 用于对角查询的布尔值
boolStat statTrue:int statFalse:int statUnknown:int = BoolStat;
// Vector
vector {t:Type} # [t] = Vector t;
tuple {t:Type} {n:#} [t] = Tuple tn;
vectorTotal {t:Type} total_count:int vector:%(Vector t) = VectorTotal t;
/////
//
// 结果类型(可能)
//
/////
resultFalse {t:Type} = Maybe t;
resultTrue {t:Type} result:t = Maybe t;
pair {X:Type} {Y:Type} a:X b:Y = Pair XY;
map {X:Type} {Y:Type} key:X value:Y = Map XY;
空值 False;
true = True;
单位 = 单位;
-
预定义标识符Type:此类型表示所有类型的类型。它通常用于指定多态类型构造函数中可选参数的类型。如果确实需要,它也可以单独使用,但在实践中这种情况非常少见。
-
标识符#:此类型用于指定 0 到 2^31-1 范围内的特殊非负整数类型;其主要用途与 相同Type。有两个内置构造函数:O:# 和S:#->#(分别为“null”和“下一个数字”),它们的工作方式与#使用模式定义 时相同。
O = #; S # = #;
-
标识符Tuple:类型 -> # -> 类型表示指定数量的指定类型的值的集合。换句话说,元组 X n表示“ n个类型为X的值的集合”。
-
该类型Bool有两个构造函数boolTrue,boolFalse用于传递布尔值。
-
False在构建 TL 模式时,可以使用无构造函数类型来代替未声明或无效的类型,因为任何尝试对该类型的值进行序列化或反序列化False都会产生错误。使用示例:
用户 {flags:#} id:flags.0?string first_name:flags.1?string last_name:flags.2?string reserved3:flags.3?False reserved4:flags.4?False = 用户标志;
用户在场 {flags:#} 信息:%(用户标志) = 用户信息标志;
用户缺席 {flags:#} = 用户信息标志;
获取用户标志:# id:int = !用户信息标志;
未来,字段中的第 3 位和第 4 位flags可能用于在更改字段名称和类型后传输新字段reserved3。reserved4这将改变user构造函数的编号,但这并不重要,因为该User flags类型仅用作裸类型。如果在这些字段实际定义之前,flags在getUser查询中传输字段中的第 3 位或第 4 位,则会导致请求的序列化/反序列化错误。
- True具有单个空构造函数的类型true的作用类似于 C/C++ 中的 void 类型。它作为裸类型%True(别名)尤其有用true,因为它的序列化长度为零。例如,first_name:flags.1?string上面使用的构造函数实际上是(尚未支持的)替代类型通用构造函数的简写first_name:(flags.1?string:true)。
当直接用于条件字段时,它可能仅指示某个空类型参数是否存在。如果条件字段存在,则关联的参数不会被填充;条件字段本身存在,其存在值可用于执行某些操作,例如:
user {flags:#} id:flags.0?string first_name:flags.1?string last_name:flags.2?string bot:flags.3?true reserved4:flags.4?False = User flags;
如果参数的第 3 位flags未设置,则用户为普通用户。如果flags参数的第 3 位已设置,则表示指定的用户是机器人:但是,在反序列化期间,该bot参数不能被赋予任何值,因为true它实际上是一个void类型。
- Unit具有单个空构造函数的类型Unit与前一种类型类似。