Как объединить два AST?

Я пытаюсь реализовать инструмент для объединения различных версий исходного кода. Учитывая две версии одного и того же исходного кода, идея будет заключаться в том, чтобы проанализировать их, сгенерировать соответствующие деревья абстрактного источника (AST) и, наконец, объединить их в один выходной источник, сохраняя грамматическую согласованность - лексер и парсер являются вопросами ANTLR: Как пропустить многострочные комментарии.

Я знаю, что есть класс ParserRuleReturnScope это помогает... но getStop() а также getStart() всегда возвращать ноль:-(

Вот фрагмент, который иллюстрирует, как я изменил свой perser, чтобы напечатать правила:

parser grammar CodeTableParser;

options {
    tokenVocab = CodeTableLexer;
    backtrack = true;
    output = AST;
}

@header {
    package ch.bsource.ice.parsers;
}

@members {
    private void log(ParserRuleReturnScope rule) {
        System.out.println("Rule: " + rule.getClass().getName());
        System.out.println("    getStart(): " + rule.getStart());
        System.out.println("    getStop(): " + rule.getStop());
        System.out.println("    getTree(): " + rule.getTree());
    }
}

parse
    : codeTabHeader codeTable endCodeTable eof { log(retval); }
    ;

codeTabHeader
    : comment CodeTabHeader^ { log(retval); }
    ;

...

2 ответа

Решение

Призыв к log(retval) в вашем коде синтаксического анализатора похоже, что это произойдет в конце правила, но это не так. Вы хотите переместить звонок в @after блок.

Я изменился log выплюнуть сообщение, а также информацию о области и добавить вызовы к нему в моей собственной грамматике, например, так:

script    
    @init {log("@init", retval);}
    @after {log("@after", retval);}
    : statement* EOF  {log("after last rule reference", retval);} 
        -> ^(STMTS statement*) 
    ;

Синтаксический тестовый ввод дал следующий результат:

Logging from @init
    getStart(): [@0,0:4='Print',<10>,1:0]
    getStop(): null
    getTree(): null
Logging from after last rule reference
    getStart(): [@0,0:4='Print',<10>,1:0]
    getStop(): null
    getTree(): null
Logging from @after
    getStart(): [@0,0:4='Print',<10>,1:0]
    getStop(): [@4,15:15='<EOF>',<-1>,1:15]
    getTree(): STMTS

Звонок в after блок имеет как stop а также tree поля заполнены.

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

Предполагая, что у вас есть AST (зачастую их трудно получить, разбор реальных языков зачастую сложнее, чем кажется), вы должны сначала определить, что у них общего, и построить отображение, собирающее эту информацию. Это не так просто, как кажется; Считаете ли вы блок кода, который был перемещен, но является тем же поддеревом, что и "общий"? Как насчет двух одинаковых поддеревьев, за исключением согласованного переименования идентификатора? Как насчет измененных комментариев? (большинство AST теряют комментарии; большинство программистов подумают, что это действительно плохая идея).

Вы можете построить вариант алгоритма "Longest Common Substring" для сравнения деревьев. Я использовал это в инструментах, которые я построил.

Наконец, после того, как вы объединили деревья, теперь вам нужно восстановить текст, в идеале сохраняя большую часть макета исходного кода. (Программисты ненавидят, когда вы изменяете макет, который они так любя производят). Таким образом, ваши AST должны собирать информацию о местоположении, а ваша регенерация должна соблюдать это там, где это возможно.

Другие вопросы по тегам