Files
BH-Python-Interpreter-2023/docs/grammar.md

7.5 KiB
Raw Blame History

Specific Python

I. 程序结构

按照控制流逐行执行。

II. 语法规则

1. 字符集合

ASCII 编码,区分大小写,中文字符是未定义的,合法字符集如下:

  • 标识符包括变量标识符、函数标识符26 个小写英语字母26 个大写英语字母,数码 09,下划线 _
  • 标准运算符:加号 +,减号 -,乘号 *,浮点除 /,整除//,取模 %
  • 关系运算符:大于 >,小于 <,大于等于 >=,小于等于 <=,不等于 !=,等于 ==
  • 增量运算符:加法 +=,减法 -=,乘法 *=,浮点除 /=,整除 //=,取模%=
  • 赋值运算符:赋值 =
  • 下标运算符:取下标对象 []
  • 优先级运算符:括号 ()
  • 分隔符:逗号 ,
  • 特殊符号:空格 ,换行符 \n,制表符 \t,注释标识符 #,字符串标识符 "'

2. 关键字

None
True
False
def
return
break
continue
if
elif
else
while
or
and
not

关键字不可作为变量名或函数名。

3. 空白字符

空格制表符在源文件中可以区分词素 (Token),同时在一行的开头可以表示缩进。

换行符表示新的语句的开始。

缩进在 Python 用于识别代码块,相关问题将在 Parser 中使用 Antlr4 解决,无需考虑相关的实现。

4. 注释

# 开始到本行结束的内容都会被作为注释。

5. 标识符

标识符的第一个字符必须是英文字母,第二个字符开始可以是英文字母、数字或者下划线。

标识符区分大小写。长度超过 64 个字符的标识符是未定义的。

6. 常量

  • 逻辑常量:True 为真,False 为假

  • 整数常量:

    • 整数常量以十进制表示,非十进制的表示为未定义行为

    • 整数常量不设负数,负数可以由正数取负号得到

    • Python 中,整数的范围是没有限制的,此处同理,这意味着你必须实现高精度整数

    • 首位为 0 的整数常量是未定义

  • 浮点数常量:

    • 浮点数常量以十进制表示
    • 浮点数常量不设负数,负数可以由正数取负号得到
    • 拥有前导 0 的浮点数为未定义的,如0001.1
    • 含有 e 的浮点数为未定义的,如 1e5
    • 以 . 开头的浮点数为未定义的,如 .123
  • 字符串常量

    • 字符串常量是由双引号 " 或单引号 ' 括起来的字符串
    • 可以由两个字符串拼接而形成新的字符串常量。如 "123""456" 相当于 "123456"
    • 字符串中的所有字符必须是可示字符 (printable character) 或空格中的一种
  • 空值常量:None 用来表示变量没有指向任何值

7. 数据类型

  • bool:只有 TrueFalse

  • int:高精度整数

  • float: 与 C++ 中的 double 一致

  • str:字符串,不可修改!

  • 元组: 具体表现请参考 python (待完善)

8. 表达式

8.1. 基础表达式

基础表达式包括单独出现的常量、变量、函数调用和数组访问。

8.2. 算术表达式

合法的算术运算符包括:+, -, *, /, //, %,具体行为可以参照 C++,特殊行为有:

  • 字符串运算符
    • str + str 表示字符串的拼接
    • str * int 表示字符串的重复
    • str <= str 表示字符串的比较,比较规则同 C++ std::striing>=, ==等同理
  • 除法
    • / 表示浮点除,即计算结果为浮点数
    • // 表示整除,即计算结果为整数。注意无论正负皆向下取整,例如 -5 // 3 = -2
    • % 表示模运算,无论模数的正负,皆定义为:a % b = a - (a // b) * b
  • 连续比较
    • 存在 1<2>3 这样连续的关系运算符,处理方法是将其拆为相邻的比较并用 and 连接但每个值最多只计算一次
    • a()<b()<c(),等价于先翻译成 a()<b() and b()<c() 再进行计算, a(),b(),c() 最多只调用一次(因为短路运算符)
  • 逻辑运算符
    • and 等价于 C++ 中的 &&or 等价于 C++ 中的 || not 等价于 C++ 中的 !
    • andor短路运算符
  • 隐式转化
    • 运算时如果两个运算数类型不一致,需转化成相同类型之后再进行运算
    • 具体转化规则参考 C++

不包含在特殊行为和 C++ 标准中的行为均为未定义行为。

8.3. 赋值表达式

  • 给一个变量赋值的意义是将这个变量指向右值,右值不管类型
  • a=1,a=”123”,a=1.1 这三条语句依次执行,再输出 a,结果是 1.1,允许变量遮蔽
  • 可以给多变量赋值,如 a,b=1,2 意思是 a=1,b=2 依次执行,此处或与标准 Python 不同
  • 增量赋值即为普通算术运算的简化表达,具体行为参考 C++

8.4. 运算符优先级

从低到高依次为(其中处在相同行的运算符优先级相同)

=
or
and
not
< > <= >= == !=
+ -
* / // %
()

9. 语句

9.1. 变量定义/赋值语句

  • 单变量赋值:var = value
  • 连续赋值:var_1 = var_2 = ... = value

参考[赋值表达式](####8.3. 赋值表达式)。

9.2. 表达式语句

参考[表达式](###8. 表达式)

9.3. 条件语句

if expression_1:
	# code block
elif expression_2:
	# code block
else expression_3:
	# code block

elif 相当于 else ifelse 可以没有。

9.4. 循环语句

while expression:
	# code block

9.5. 跳转语句

  • 包括 returnbreak,和 continue
  • 具体行为参考 C++

10. 函数

10.1. 函数定义

def func(parameters):
	# code block
  • 参数列表如 a, b, c,变量名之间用逗号分隔,可以为空

  • 有些变量可以有默认值,但是都必须出现在无默认值的变量后面

  • 函数可以有返回值,也可以没有返回值,无需显示声明。返回值可以为多变量,如 return a, b

10.2. 函数调用

func(parameters)
  • 函数调用必须出现在该函数定义后。

  • 参数有两种形式keyword 和 positional

    • Keyword argument 比如 foo(a=1,b=2) 表示传入参数 a 的值为 1b 的值 2

    • Positional argument 是指 foo(1,2) 这样按照出现顺序指代参数

    • 若一个参数列表中同时有两种参数形式,那么 positional argument 必须出现在 keyword argument 之前。如 foo(1,b=2)

  • 数据保证递归层数不超过 2000

11. 内建函数

  • print:输出,可以有任意个参数,逐个输出,中间用空格分隔。输出后换行。输出 float 保留 6 位小数。输出字符串不要输出前后面的引号。如 print("123",1.0) 请输出 123 1.000000

  • int:将 floatboolstr 转成 int

  • float: 将 intboolstr 转成 float

  • strintfloatbool 转成 str

  • boolintfloatstr 转成 bool。对于 str,如果是 "" 则为 False,否则为 True

  • 转型类函数都只有一个参数。

12. 作用域

作用域相关规则:

  • 与标准 Python 不同,全局变量的生效范围是全部范围(不用 global 关键字即可访问)
  • 只有函数的调用会产生新的作用域,作用域的产生请参考 Python
  • 没有代码块级作用域,变量注销只发生在函数级作用域退出时
  • 全局作用域无需global即可访问因此局部变量覆盖全局变量的唯一方式是通过函数参数列表覆盖。