Python词法分析 - 逻辑行和复合语句

Mar*_*icu 8 python bnf python-3.x python-internals

如果这是一个愚蠢的问题,请提前道歉,但我似乎无法理解以下内容

所以我理解:

逻辑行的结尾由令牌NEWLINE表示

这意味着定义Python语法的方式是结束逻辑行的唯一方法是使用\n令牌.

物理线也是如此(相当于EOL,这是您在编写文件时使用的平台的EOL,但仍然\n通过Python 转换为通用.

逻辑行可以或不可以等同于一个或多个物理行,但通常它是一个,并且大多数情况下,如果您编写干净的代码,它就是一个.

在某种意义上说:

foo = 'some_value'  # 1 logical line = 1 physical  
foo, bar, baz = 'their', 'corresponding', 'values'  # 1 logical line = 1 physical
some_var, another_var = 10, 10; print(some_var, another_var); some_fn_call()

# the above is still still 1 logical line = 1 physical line
# because ; is not a terminator per se but a delimiter
# since Python doesn't use EBNF exactly but rather a modified form of BNF

# p.s one should never write code as the last line, it's just for educational purposes
Run Code Online (Sandbox Code Playgroud)

如果没有显示1逻辑相当于> 1物理的示例,我的问题是文档中的以下部分:

语句不能越过逻辑行边界,除非语法允许NEWLINE(例如,复合语句中的语句之间)

但这甚至意味着什么呢?我理解复合语句的列表,如:if,while,for等,它们都由一个或多个子句组成,每个子句又由一个头一个套件组成.该套件由一个或多个语句组成,让我们举一个更具体的例子:

所以if语句根据语法是这样的(不包括elifs和else子句):

if_stmt ::=  "if" expression ":" suite
Run Code Online (Sandbox Code Playgroud)

套件及其后续声明:

suite         ::=  stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement     ::=  stmt_list NEWLINE | compound_stmt
stmt_list     ::=  simple_stmt (";" simple_stmt)* [";"]
Run Code Online (Sandbox Code Playgroud)

所以这意味着如果你想要你可以选择(由"|"给出)你的套房是2种方式之一:

  1. 在同一行:

    缺点:不是pythonic,你不能有另一个引入新块的复合语句(如func def,另一个if等)

    advatanges:我想是一个班轮

例:

if 'truthy_string': foo, bar, baz = 1, 2, 3; print('whatever'); call_some_fn();
Run Code Online (Sandbox Code Playgroud)
  1. 介绍一个新的块:

    优点:全部,以及正确的方法

例:

if 'truthy_value':
    first_stmt = 5
    second_stmt = 10
    a, b, c = 1, 2, 3
    func_call()
    result = inception(nested(calls(one_param), another_param), yet_another))
Run Code Online (Sandbox Code Playgroud)

但我不知道怎么做

除非语法允许NEWLINE,否则语句不能跨越逻辑行边界

我在上面看到的是一个套件,它是由if子句控制的代码块,反过来,该套件由逻辑的独立行(语句)组成,其中每个逻辑行是一个物理行(巧合) .我没有看到一条逻辑线如何跨越边界(这基本上只是结尾的一个奇特的词,限制,这是换行符),我不知道一个语句如何跨越这些边界并跨越到下一个声明,或者我真的很困惑,一切都搞砸了,但如果有人可以请解释.

感谢您提前的时间.

MSe*_*ert 4

Python语法

幸运的是,Python 文档中有完整语法规范。

该规范中的语句定义为:

stmt: simple_stmt | compound_stmt
Run Code Online (Sandbox Code Playgroud)

NEWLINE逻辑行由(这不在规范中,但基于您的问题)分隔。

一步步

好的,我们来看看这个,规格是什么

simple_stmt

simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
Run Code Online (Sandbox Code Playgroud)

好吧,现在它进入了几个不同的路径,单独遍历所有路径可能没有意义,但根据规范,如果任何s 包含 a (目前不包含,可以) ,则 asimple_stmt 可能会跨越逻辑行边界。small_stmtNEWLINE

除了理论上的可能性之外,实际上还有

compound_stmt

compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
[...]
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
[...]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
Run Code Online (Sandbox Code Playgroud)

我只选择了这个if声明,suite因为它已经足够了。包含and以及其中所有内容的if语句是一个语句(复合语句)。并且因为它可能包含s (如果不只是 a ),它已经满足“跨越逻辑行边界的语句”的要求。 elifelseNEWLINEsuitesimple_stmt

示例if(示意图):

if 1:
    100
    200
Run Code Online (Sandbox Code Playgroud)

将会:

if_stmt
|---> test        --> 1
|---> NEWLINE
|---> INDENT
|---> expr_stmt   --> 100
|---> NEWLINE
|---> expr_stmt   --> 200
|---> NEWLINE
|---> DEDENT
Run Code Online (Sandbox Code Playgroud)

所有这些都属于 if 语句(并且它不仅仅是由 or “控制”的块ifwhile...)。

ifparsersymbol和相同token

一种可视化的方法是使用内置的parser,tokensymbol模块(实际上,在写答案之前我还不知道这个模块):

import symbol
import parser
import token

s = """
if 1:
    100
    200
"""
st = parser.suite(s)

def recursive_print(inp, level=0):
    for idx, item in enumerate(inp):
        if isinstance(item, int):
            print('.'*level, symbol.sym_name.get(item, token.tok_name.get(item, item)), sep="")
        elif isinstance(item, list):
            recursive_print(item, level+1)
        else:
            print('.'*level, repr(item), sep="")

recursive_print(st.tolist())
Run Code Online (Sandbox Code Playgroud)

实际上,我无法解释大部分结果parser,但它表明(如果您删除了很多不必要的行),包括suite它的换行符确实属于if_stmt. 缩进表示解析器在特定点的“深度”。

file_input
.stmt
..compound_stmt
...if_stmt
....NAME
....'if'
....test
.........expr
...................NUMBER
...................'1'
....COLON
....suite
.....NEWLINE
.....INDENT
.....stmt
...............expr
.........................NUMBER
.........................'100'
.......NEWLINE
.....stmt
...............expr
.........................NUMBER
.........................'200'
.......NEWLINE
.....DEDENT
.NEWLINE
.ENDMARKER
Run Code Online (Sandbox Code Playgroud)

这可能会变得更加美丽,但我希望它即使在目前的形式下也能起到说明作用。