59

Я читал немного о том, как работают интерпретаторы/компиляторы, и одна область, где меня путают, - это разница между AST и CST. Я понимаю, что парсер делает КНТ, передает его семантическому анализатору, который превращает его в АСТ. Однако я понимаю, что семантический анализатор просто гарантирует соблюдение правил. Я действительно не понимаю, почему он действительно внесет какие-либо изменения, чтобы сделать его абстрактным, а не конкретным.В чем разница между абстрактным деревом синтаксиса и деревом синтаксиса?

Есть ли что-то, что мне не хватает в семантическом анализаторе, или разница между AST и CST несколько искусственна?

ответ

17

This blog post может быть полезно.

Мне кажется, что AST «отбрасывает» много промежуточной грамматической/структурной информации, которая не будет способствовать семантике. Например, вам все равно, что 3 является атомом, является фактором, который является фактором ... Вам просто нужно, чтобы это было 3, когда вы выполняете выражение экспоненциального выражения или что-то еще.

9

конкретное дерево синтаксиса следует правилам грамматики языка. В грамматике «списки выражений», как правило, определяются с двумя правилами

  • expression_list может быть: выражение
  • expression_list может быть: выражение, expression_list

Последовал в буквальном смысле, эти два правила дают расческу формы в любой список выражений, который появляется в программе.

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

45

Конкретное дерево синтаксиса представляет исходный текст точно в анализируемой форме. В общем, он соответствует контекстно-свободной грамматике, определяющей исходный язык.

Однако в конкретной грамматике и дереве есть много вещей, которые необходимы для того, чтобы текст источника однозначно обрабатывался, но не вносил вклад в реальный смысл. Например, чтобы реализовать приоритет оператора, ваш CFG обычно имеет несколько уровней компонентов выражения (термин, коэффициент и т. Д.) С операторами, соединяющими их на разных уровнях (вы добавляете термины для получения выражений, термины состоят из факторов, , и т.д.). Чтобы на самом деле интерпретировать или компилировать язык, вам это не нужно; вам просто нужны узлы Expression, у которых есть операторы и операнды. Абстрактное синтаксическое дерево является результатом упрощения конкретного дерева синтаксиса вплоть до того, что действительно необходимо для представления значения программы. Это дерево имеет гораздо более простое определение и, следовательно, его легче обрабатывать на более поздних этапах выполнения.

Обычно вам не нужно строить конкретное дерево синтаксиса. Процедуры действий в вашей грамматике YACC (или Antlr, или Menhir, или что-то еще ...) могут непосредственно строить абстрактное синтаксическое дерево, поэтому конкретное дерево синтаксиса существует только как концептуальный объект, представляющий структуру анализа исходного текста.

+0

поэтому мы создаем настоящий АСТ в коде? – Joey

1

Конкретное дерево синтаксиса содержит всю информацию, такую ​​как лишние скобки и пробелы и комментарии, абстрактное синтаксическое дерево абстрагирует от этой информации.

 

NB: достаточно смешно, когда вы реализуете рефакторинг двигатель вашего AST будет снова содержать всю конкретную информацию, но вы будете держать в виде его как AST, потому что стал стандартным термином в поле (так можно было сказать, что он давно потерял свое первоначальное значение).

+0

Ну, у него может быть не вся конкретная информация. Все, что требуется, это то, что он сможет восстановить эту информацию. См. Мой ответ. –

+0

Комментировать вчера? SO ошибка или есть значок некроманта комментариев, который должен быть получен, о котором я не знаю? :) (PS: но приятно слышать от вас, вы только что наткнулись на ваш разговор в Google Tech о DMS ...) – akuhn

27

A бетонное синтаксическое дерево соответствует тому, что говорят правила грамматики, является синтаксисом. Цель абстрактного синтаксического дерева имеет «простое» представление того, что необходимо в «синтаксическом дереве».

Реальное значение в AST IMHO заключается в том, что оно является меньшим, чем CST, и поэтому занимает меньше времени для обработки. (Можно сказать, кому это нужно? Но я работаю с инструментом, где у нас есть десятки миллионов узлов, живущих сразу!).

Большинство генераторов парсеров, которые имеют любую поддержку для построения синтаксических деревьев, настаивают на том, что вы точно определяете, как они создаются в предположении, что ваши узлы дерева будут «проще», чем CST (и в этом они, как правило, поскольку программисты довольно ленивы). Возможно, это означает, что вам нужно закодировать меньше функций посетителя дерева, и это тоже ценно, поскольку оно минимизирует инженерную энергию. Если у вас есть 3500 правил (например, для COBOL), это имеет значение. И это «более простое» приводит к хорошей собственности «малости».

Но наличие таких АСТ создает проблему, которой ее не было: она не соответствует грамматике, и теперь вы должны мысленно отслеживать их оба. И когда есть 1500 узлов AST для 3500 грамматики правил, это очень важно. И если грамматика эволюционирует (они всегда делают!), Теперь у вас есть два гигантских набора вещей, чтобы синхронизировать их.

Другое решение - позволить парсеру просто создавать узлы CST для вас и просто использовать их. Это огромное преимущество при построении грамматик: нет необходимости изобретать 1500 специальных узлов АСТ для моделирования правил грамматики 3500. Подумайте о том, что дерево изоморфно грамматике. С точки зрения инженера-грамматики это совершенно безмозгло, что позволяет ему сосредоточиться на правильном использовании грамматики и взломать ее до глубины души. Возможно, вам нужно написать больше правил для посетителей узлов, но это можно управлять. Подробнее об этом позже.

Что мы делаем с DMS Software Reengineering Toolkit - это автоматическое построение CST на основе результатов анализа (GLR). DMS автоматически строит «сжатый» CST по соображениям экономии пространства, за счет устранения не-балансовая стоимость терминалов (ключевые слова, точечностью), семантически бесполезные одинарные производств и формирования списков для пар правил грамматики, которые список, как:

L = e ; 
    L = L e ; 
    L2 = e2 ; 
    L2 = L2 ',' e2 ; 

и широкий спектр вариантов таких форм. Вы думаете с точки зрения правил грамматики и виртуального КНТ; инструмент работает с сжатым представлением. Легко на вашем мозгу, быстрее/меньше во время работы.

Замечательно, что сжатый CST, построенный таким образом, выглядит много AST, который вы, возможно, разработали вручную (см. Ссылку в конце на примеры). В частности, сжатый CST не несет никаких узлов, которые являются просто конкретным синтаксисом. Есть небольшие кусочки неловкости: например, в то время как конкретные узлы для '(' и ')', классически найденные в подграфах выражений, не находятся в дереве, в скобках CST появляется «круглые скобки» и должен быть обрабатываются. Истинный АСТ не имел бы этого. Это похоже на довольно небольшую цену, чтобы заплатить за удобство, чтобы не указывать конструкцию AST. И документация для дерева всегда доступна и правильная: грамматика - это документация.

Как избежать «дополнительных посетителей»? Мы не полностью, но DMS предоставляет библиотеку AST, которая проходит AST и обрабатывает различия между CST и AST прозрачно. DMS также предлагает оценщик грамматики атрибутов (AGE), который является методом передачи значений, вычисляемых узлами вверх и вниз по дереву; AGE обрабатывает все проблемы с представлением дерева, и поэтому инженер-программист беспокоится о том, чтобы непосредственно писать вычисления непосредственно по самим правилам грамматики. Наконец, DMS также предоставляет шаблоны «поверхностный синтаксис», которые позволяют фрагментам кода из грамматики использоваться для поиска определенных типов поддеревьев, не зная большинства задействованных типов узлов.

Один из других ответов отмечает, что если вы хотите создавать инструменты, которые могут восстанавливать источник, ваш АСТ должен соответствовать КНТ. Это не совсем так, но гораздо проще восстановить источник, если у вас есть узлы CST. DMS generates most of the prettyprinter automatically, поскольку он имеет доступ к обоим: -}

Итог: АСТы хороши для малых, как фискальных, так и концептуальных. Автоматизированная конструкция AST из CST обеспечивает и то и другое, и позволяет избежать проблемы отслеживания двух разных наборов.

EDIT марта 2015: Link to examples of CST vs. "AST" built this way

1

Это различие, которое не делает разницы.

АСТ обычно объясняется как способ приближения семантики выражения языка программирования путем выброса лексического содержимого. Например в контекстно-свободной грамматике вы можете написать следующее правило EBNF

term: atom (('*' | '/') term)* 

, тогда как в случае AST использовать только mul_rule и div_rule, выражающие соответствующие арифметические операции.

Не могут ли эти правила быть введены в грамматике в первую очередь? Конечно. Вы можете переписать эти компактные и абстрактной правило, разбив его в более конкретных правил, используемых для имитации указанных AST узлов:

term: mul_rule | div_rule 
mul_rule: atom ('*' term)* 
div_rule: atom ('/' term)* 

Теперь, когда вы думаете, с точки зрения сверху вниз разбором то второй термин представляет ПЕРВЫЙ/ПЕРВЫЙ конфликт между mul_rule и div_rule что-то, с чем не может разбираться парсер LL (1). Первая форма правила была левой факторизированной версией второй, которая эффективно устраняла структуру. Вы должны заплатить некоторый приз за использование LL (1) здесь.

Таким образом, АСТ являются специальным дополнением, используемым для устранения недостатков грамматик и парсеров. Трансформация CST -> AST - это рефакторинг. Никто никогда не беспокоился, когда в дереве синтаксиса хранится дополнительная запятая или двоеточие. Напротив, некоторые авторы модифицируют их в АСТ, потому что они любят использовать АСТ для проведения рефакторинга вместо того, чтобы одновременно поддерживать различные деревья или писать дополнительный механизм вывода.Программисты ленивы по уважительным причинам. Фактически они сохраняют даже информацию о строках и столбцах, собранную лексическим анализом в AST для отчета об ошибках. Очень абстрактно.

11

Это основано на грамматике Expression Evaluator Терренсом Парром.

Грамматика для этого примера:

grammar Expr002; 

options 
{ 
    output=AST; 
    ASTLabelType=CommonTree; // type of $stat.tree ref etc... 
} 

prog : (stat)+ ; 

stat : expr NEWLINE  -> expr 
     | ID '=' expr NEWLINE -> ^('=' ID expr) 
     | NEWLINE    -> 
     ; 

expr : multExpr (('+'^ | '-'^) multExpr)* 
     ; 

multExpr 
     : atom ('*'^ atom)* 
     ; 

atom : INT 
     | ID 
     | '('! expr ')'! 
     ; 

ID  : ('a'..'z' | 'A'..'Z')+ ; 
INT  : '0'..'9'+ ; 
NEWLINE : '\r'? '\n' ; 
WS  : (' ' | '\t')+ { skip(); } ; 

Входной

x=1 
y=2 
3*(x+y) 

Дерево синтаксического разбора

Дерево разбора представляет собой конкретное представление входного сигнала. Дерево разбора сохраняет всю информацию ввода. Пустые поля представляют пробелы, т. Е. Конец строки.

Parse Tree

АСТ

АСТ является абстрактным представлением входного сигнала. Обратите внимание, что в АСТ нет парен, потому что ассоциации выводятся из древовидной структуры.

AST

EDIT

Для более через пояснения см Compilers and Compiler Generators стр. 23

0

Просто, AST содержит только семантику кода, дерево разбора/CST также содержит информацию о том, как именно был написан код.