内部如何使用Python语法?

时间:2013-10-13 22:31:36

标签: python python-3.x grammar language-design

我正在努力深入了解Python的工作原理,我一直在研究http://docs.python.org/3.3/reference/grammar.html所示的语法。

我注意到它说你也必须改变parsermodule.c,但说实话我只是不跟踪这里发生了什么。

我理解语法是如何阅读语言的规范,但是......我甚至不知道这是什么写的。它看起来几乎像Python,但它不是。

我希望更好地理解这个规范以及Python如何在内部使用它来做....什么取决于它(答案就是一切,但我的意思是“引擎”的哪个方面正在处理它),使用它的是什么,它如何与编译/运行脚本相关联?

很难相信整个语言都归结为两页规范...

4 个答案:

答案 0 :(得分:13)

语法用于描述语言中所有可能的字符串。它在指定解析器应如何解析语言时也很有用。

在这个语法中,似乎他们使用自己的EBNF版本,其中非终端是任何小写单词,终端全部是大写或用引号括起来。例如,NEWLINE是终端,arith_expr是非终端,“if”也是终端。任何非终端都可以被它的相应生产规则的冒号右边的任何东西替换。例如,如果您查看第一条规则:

  

single_input:NEWLINE | simple_stmt | compound_stmt NEWLINE

我们可以用NEWLINE,simple_stmt或者compound_stmt中的一个替换single_input,后跟NEWLINE。假设我们用“compound_stmt NEWLINE”替换它,那么我们将寻找compound_stmt的生产规则:

  

compound_stmt:if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef |装饰

并选择我们要使用的其中一个,并将其替换为“compound_stmt”(在其中保留NEWLINE)

假设我们想要生成有效的python程序:

if 5 < 2 + 3 or not 1 == 5:
    raise

我们可以使用以下推导:

  1. single_input
  2. compound_stmt NEWLINE
  3. if_stmt NEWLINE
  4. 'if'test':'suite NEWLINE
  5. 'if'or_test':'NEWLINE INDENT stmt stmt DEDENT NEWLINE
  6. 'if'and_test'或'and_test':'NEWLINE INDENT simple_stmt DEDENT NEWLINE
  7. 'if'not_test'或'not_test':'NEWLINE INDENT small_stmt DEDENT NEWLINE
  8. '如果'比较'或''不'not_test':'NEWLINE INDENT flow_stmt DEDENT NEWLINE
  9. 'if'expr comp_op expr'或''not'comparative':'NEWLINE INDENT raise_stmt DEDENT NEWLINE
  10. 'if'arith_expr'&lt;' arith_expr'或''not'arith_expr comp_op arith_expr':'NEWLINE INDENT'提高'DEDENT NEWLINE
  11. 'if'term'&lt;'术语'+'术语'或''不'arith_expr == arith_expr':'NEWLINE INDENT'引发'DEDENT NEWLINE
  12. 'if'NUMBER'&lt;' NUMBER'+'NUMBER'或''not'NUMBER == NUM​​BER':'NEWLINE INDENT'提高'DEDENT NEWLINE
  13. 这里有几点注意事项,首先,我们必须从一个被列为起始非终端的非终端开始。在该页面中,它们将它们列为single_input,file_input或eval_input。其次,一旦所有符号都是终点(因此名称),就完成推导。第三,更常见的是每行进行一次替换,为了简洁起见,我立即做了所有可能的替换并开始跳过接近结束的步骤。

    鉴于语言中的字符串,我们如何找到它的派生?这是解析器的工作。解析器反向设计生产序列,首先检查它确实是一个有效的字符串,以及如何从语法中导出它。值得注意的是,许多语法可以描述单一语言。但是,对于给定的字符串,它的推导当然对于每个语法都是不同的。所以从技术上讲,我们为语法而不是语言编写解析器。一些语法更容易解析,一些语法更容易阅读/理解。这个属于前者。

    这也没有指定整个语言,只是它的外观。语法没有提到语义。

    如果您对解析和语法更感兴趣,我建议Grune, Jacobs - Parsing Techniques。它是免费的,也适合自学。

答案 1 :(得分:8)

python语法 - 与大多数其他语法一样 - 以BNF Backus-Naur形式给出。尝试阅读如何阅读它,但基本结构是:

<something> ::= (<something defined elsewhere> | [some fixed things]) [...]

这被视为<something> 被定义为 something else 重复多次的固定内容。

BNF基于近2000年的格式来描述语言的允许结构,非常简洁,并且将描述给定语言中所有允许的结构,不一定是所有那些有意义的结构

实施例

基本算术可以描述为:

<simple arithmetic expression> ::= <numeric expr>[ ]...(<operator>[ ]...<numeric expr>|<simple arithmetic expression>)
<numeric expr> ::= [<sign>]<digit>[...][.<digit>[...]]
<sign> ::= +|-
<operator> ::= [+-*/]
<digit> ::= [0123456789]

其中说一个简单的算术运算是一个可选的有符号数字,由一个或多个数字组成,可能带有一个小数点和一个或多个后续数字,后面可以跟空格,后跟{{{ 1}},可选地后跟空格,后跟数字或其他简单算术运算,即后跟数字等等。

这描述了几乎所有的基本算术运算,并且可以扩展为包含函数等。请注意,它允许无效的有效语法操作,例如:{{1}即使结果无效,语法也是有效的。

有时可能会让您意识到您可能没有想到的操作,例如:+-*/22.34 / -0.0的有效操作,但56+-50不是。

请注意,SGMLXML / Schema都是相关的,但描述任何语言结构的方法不同。 YAML是另一种描述计算机特定语言中允许结构的方法。

免责声明:我的BNF有点生疏,所以如果我在上述道歉中犯了重大错误,请纠正我。

答案 2 :(得分:5)

这基本上是EBNF(扩展Backus-Naur形式)规范。

答案 3 :(得分:4)

当您使用某种语言编写程序时,您的解释器/编译器为了从一系列字符转换为实际操作而必须做的第一件事就是将该字符序列转换为更高复杂度的结构。为此,首先按照表示每个“单词”代表的标记序列对程序进行分块。例如,构造

if foo == 3: print 'hello'

将转换为

1,0-1,2:    NAME    'if'
1,3-1,6:    NAME    'foo'
1,7-1,9:    OP  '=='
1,10-1,11:  NUMBER  '3'
1,11-1,12:  OP  ':'
1,13-1,18:  NAME    'print'
1,19-1,26:  STRING  "'hello'"
2,0-2,0:    ENDMARKER   ''

但请注意,即使是“if if if”也可以正确地制作成代币

1,0-1,2:    NAME    'if'
1,3-1,5:    NAME    'if'
1,6-1,8:    NAME    'if'
1,9-1,11:   NAME    'if'
2,0-2,0:    ENDMARKER   ''

标记化之后的是解析为更高级别的结构,该结构分析令牌是否实际上合在一起,后者的例子没有,但第一个做的。为此,解析器必须识别标记的实际含义(例如,if是关键字,foo是变量),然后从标记中构建树,在层次结构中组织它们并查看此层次结构是否实际生成感。这就是你所看到的语法的来源。这个语法是在BNF中,这是一种表达语言可以识别的结构的符号。该语法被一个程序(例如,野牛)消化,该程序具有获取该语法并产生实际C代码的神奇属性,通常通过识别标记,组织它们,返回一个解析树,或告诉你哪里有错误。

简短版本:开发一种语言是关于定义令牌以及如何将这些令牌放在一起以提供有意义的东西。这是通过语法完成的,您可以使用该语法使用自动化工具生成实际的“解析器”代码。