Подтвердить что ты не робот

Как используется грамматика Python внутри?

Я пытаюсь получить более глубокое понимание того, как работает Python, и я смотрел грамматику, показанную на http://docs.python.org/3.3/reference/grammar.html.

Я замечаю, что он говорит, что вам придется также менять parsermodule.c, но, честно говоря, я просто не понимаю, что происходит здесь.

Я понимаю, что грамматика - это спецификация того, как читать язык, но... Я даже не могу сказать, что это написано. Он выглядит почти как Python, но это не так.

Я ищу, чтобы лучше понять эту спецификацию и как она используется внутри Python, чтобы... что-то делать. Что от этого зависит (ответ - это все, но я имею в виду конкретно, какой аспект "движка" его обрабатывает), что его использует, как он связан с компиляцией/запуском script?

Сложно поверить, что весь язык сводится к спецификации двух страниц...

4b9b3361

Ответ 1

Грамматика используется для описания всех возможных строк на языке. Это также полезно при определении того, как синтаксический анализатор должен анализировать язык.

В этой грамматике кажется, что они используют свою собственную версию EBNF, где non-terminal - любое строчное слово, а терминал - все прописные или окруженные кавычками. Например, NEWLINE является терминалом, arith_expr является нетерминальным, а "if" также является терминалом. Любой нетерминал может быть заменен чем угодно справа от двоеточия его соответствующего правила производства. Например, если вы посмотрите на первое правило:

single_input: NEWLINE | simple_stmt | composite_stmt NEWLINE

Мы можем заменить single_input одним из NEWLINE, simple_stmt или complex_stmt, за которым следует NEWLINE. Предположим, мы заменили его на "compound_stmt NEWLINE", тогда мы будем искать производственное правило для complex_stmt:

compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | украшенный

и выберите, какой из них мы хотим использовать, и заменим его на "составной_stmt" (сохраняя в нем NEWLINE)

Предположим, мы хотели сгенерировать действительную программу python:

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

Мы могли бы использовать следующий вывод:

  • single_input
  • compound_stmt NEWLINE
  • if_stmt NEWLINE
  • 'if' test ':' suite NEWLINE
  • 'if' or_test ':' NEWLINE INDENT stmt stmt DEDENT NEWLINE
  • 'if' and_test 'или' and_test ':' NEWLINE INDENT simple_stmt DEDENT NEWLINE
  • 'if' not_test 'или' not_test ':' NEWLINE INDENT small_stmt DEDENT NEWLINE
  • ', если' сравнение 'или' 'not' not_test ':' NEWLINE INDENT flow_stmt DEDENT NEWLINE
  • 'if' expr comp_op expr 'или' 'не' сравнение ':' NEWLINE INDENT raise_stmt DEDENT NEWLINE
  • 'if' arith_expr '<' arith_expr 'или' 'not' arith_expr comp_op arith_expr ':' NEWLINE INDENT 'raise' DEDENT NEWLINE
  • ', если' term '<' term '+' term 'или' 'not' arith_expr == arith_expr ':' NEWLINE INDENT 'raise' DEDENT NEWLINE
  • ', если' NUMBER '<' NUMBER '+' NUMBER 'или' 'not' NUMBER == NUMBER ':' NEWLINE INDENT 'raise' DEDENT NEWLINE

Несколько примечаний здесь, во-первых, мы должны начать с одного из не-терминалов, который указан как стартовый нетерминал. На этой странице они перечисляют их как single_input, file_input или eval_input. Во-вторых, вывод завершается после того, как все символы являются терминальными (отсюда и название). В-третьих, чаще всего делается одна подстановка на линию, для краткости я делал все возможные замены сразу и начал пропускать шаги ближе к концу.

С учетом строки на языке, как найти ее вывод? Это работа парсера. Парсер обращает инженеров производственную последовательность, чтобы сначала проверить, что это действительно допустимая строка, и, кроме того, как она может быть получена из грамматики. Стоит отметить, что многие грамматики могут описывать один язык. Однако для данной строки ее вывод, конечно, будет отличаться для каждой грамматики. Поэтому технически мы пишем парсер для грамматики, а не языка. Некоторые грамматики легче разбирать, некоторые грамматики легче читать/понимать. Это принадлежит первому.

Также это не указывает весь язык, как он выглядит. Грамматика ничего не говорит о семантике.

Если вас больше интересует разбор и грамматика, я рекомендую Grune, Jacobs - Parsing Techniques. Это бесплатно и хорошо для самостоятельного изучения.

Ответ 2

Грамматика питона - как и большинство других - представлена ​​в BNF или Бэксу-Науре Форма. Попробуйте прочитать, как читать, но основная структура:

<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]

В нем говорится, что простая арифметическая операция - это, необязательно, подписанное число, состоящее из одной или нескольких цифр, возможно с десятичной точкой и одной или несколькими последующими цифрами, необязательно сопровождаемыми пробелами, за которыми следует ровно один из +-*/, необязательно сопровождаемое пробелами, за которым следует либо число, либо другая простая арифметическая операция, то есть число, за которым следует и т.д.

Здесь описываются все основные арифметические операции и могут быть расширены для включения функций и т.д. Обратите внимание, что это позволяет использовать недопустимые операции, которые являются допустимым синтаксисом, например: 22.34 / -0.0 является синтаксическим, хотя результат недействительно.

Иногда вы можете знать, что возможны операции, о которых вы, возможно, и не подумали, например: 56+-50 является действительной операцией, такой как 2*-10, но 2*/3 не является.

Обратите внимание, что SGML и XML/Schema являются связанными, но разными методологиями для описания структуры любого языка. YAML - это еще один способ описания разрешенных структур на компьютере определенных языках.

Отказ от ответственности: мой BNF немного ржавый, поэтому, если я допустил какие-либо серьезные ошибки в приведенных выше извинениях и, пожалуйста, исправьте меня.

Ответ 3

Это в основном форма EBNF (Extended Backus-Naur Form).

Ответ 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 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, которая представляет собой обозначение для выражения конструкций, которые может распознать язык. Эта грамматика переваривается программой (например, bison), которая обладает магическим свойством принимать эту грамматику и генерировать фактический код C, который делает тяжелую работу для вас, обычно, распознавая маркеры, организуя их, возвращая вам дерево разбора, или сообщите, где там ошибка.

Краткая версия: разработка языка - это определение маркеров и то, как эти токены объединяются, чтобы дать что-то значимое. Это делается с помощью грамматики, которую вы используете для генерации фактического кода "парсер" с помощью автоматизированных инструментов.