Можно ли использовать ANTLR с JastAdd? - PullRequest
0 голосов
/ 18 июня 2019

Я пытаюсь использовать систему JastAdd с генератором синтаксического анализатора ANTLR (v4.7.2). Я использовал его с JJTree / JavaCC, в котором JJTree автоматически создает AST, но я хочу использовать ANTLR, потому что считаю документацию более надежной, а систему в целом более надежной. Однако я не могу понять, как связать AST, сгенерированный ANTLR, с JastAdd. Я также новичок в использовании этих ANTLR, JastAdd и ANT, поэтому, если я сделал или сказал что-то глупое, пожалуйста, будьте терпеливы.

В течение нескольких дней я искал примеры использования ANTLR с JastAdd, но не нашел ни одного. Я полагаю, что это должно быть возможно, поскольку JastAdd упоминает, что с ним можно использовать любой генератор синтаксического анализатора на основе Java. Я уже знаю, как построить AST (из этого поста: Как создать AST с ANTLR4? ), и сделал это для очень простой грамматики, чтобы начать развивать понимание. Я не понимаю, как связать AST, сгенерированный в моем коде ANTLR, с JastAdd.

Я попытался выяснить, как это сделать при использовании JavaCC / JJTree, чтобы посмотреть, смогу ли я выяснить, как это делается. Из того, что я могу сказать, имена классов узлов AST, генерируемых JJTree, должны совпадать с именами нетерминалов в файле .ast, который используется JastAdd для создания классов для каждого типа узла. Я верю в это, потому что под заголовком «Parsing» в README (http://jastadd.org/releases/jastadd2/R20130412/readme.php), JastAdd упоминается «JavaCC и его расширение для построения деревьев, JJTree, используются для синтаксического анализа. Они предполагают, что верхние классы называются Node и SimpleNode. Обычно JJTree генерирует свои собственные подклассы узла AST, но мы «обманываем» его, чтобы вместо этого использовать классы AST, сгенерированные JastAdd (генерируя их перед запуском JavaCC / JJTree). «Поэтому я дал те же имена моим классам, которые представляют узлы AST и нетерминалы в файле .ast (за исключением нетерминалов, начинающихся с abstract operand в строках 5-7 ArithmeticParser.ast, поскольку они не являются узлами, но вы не можете представлять выбор между токенами в файле .ast в любом насколько я могу судить иначе. Я не слишком уверен, как справиться с этим, может быть, мне нужно добавить больше классов AST?). Но я не могу сказать, есть ли что-то еще, что мне нужно сделать, чтобы связать их

Еще одна вещь, которую я увидел в README под заголовком «Понимание реализации», заключается в том, что JastAdd создает частичный AST из файла .ast, затем в конечном итоге файлы аспектов .jrag добавляются в AST, и в конце AST классы генерируются. Несколько вещей смущают меня по этому поводу: (1) Здесь ничего не говорится об использовании AST, сгенерированного любым используемым генератором синтаксического анализатора (будь то ANTLR, JJTree / JavaCC и т. Д.), Поэтому я не совсем понимаю, как это работает в JastAdd. (2) Если мы генерируем классы AST в конце, как он начинает строить AST до этого?

Я также хочу использовать ant для сборки проекта (и не знаю, что делать, чтобы собрать проект вручную). Я посмотрел файл build.xml примера построения дерева (http://jastadd.cs.lth.se/examples/CalcTree/index.html),) и попытался адаптировать его для использования с ANTLR, но мне это не удалось. Я получаю следующую ошибку: /Users/Blake/Documents/Dataview_Files/GrammarExANTLR/build.xml:34: Unable to determine generated class.

Я прочитал то, что мне удалось найти об этом в Интернете ( Почему моя задача Ant по сборке ANTLR завершается с ошибкой «Невозможно определить сгенерированный класс»? * ) и (http://palove.kadeco.sk/itblog/posts/40). В первой ссылке есть комментарий:

"Если вы выполняете ant -diagnostics в своей оболочке, в списке ANT_HOME / lib jar должны быть только ant-antlr.jar и ant-antlr3.jar. Если есть также antlr2? .Jar, попробуйте удалить его. "

Я сделал это, и у меня есть только ant-antlr.jar, нет ant-antlr2.jar, ant-antlr3.jar или ant-antlr4.jar. Кто-нибудь знает, где можно взять ant-antlr4.jar (если он вообще существует)? Я пока не смог построить проект, что еще больше мешает мне связать свой AST с JastAdd.

Итак, чтобы подвести итог, мои вопросы:

  1. Как я могу использовать мой AST, сгенерированный с помощью ANTLR с JastAdd?

  2. Как я могу использовать муравья с ANTLR?

  3. Есть ли примеры использования ANTLR с JastAdd?

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

Грамматика (ArithmeticParser.g4):


/** TOKENS - name must begin with uppercase letter */
// must wrap in () to use -> skip command
WHITESPACE : (' ' | '\t' | '\n' | '\r') -> skip;
EQ : 'equals' | '=' | '->';
OPERATOR : PLUS | MINUS | MULT | DIV | MOD | EXP ;
// these are fragments, meaning they can only be used by LEXER not PARSER
fragment PLUS : 'plus' | '+';
fragment MINUS : 'minus' | '-';
fragment MULT : 'times' | '*';
fragment DIV : 'divby' | '/';
fragment MOD : 'mod' | '%';
fragment EXP : 'pow' | '^';
INT : DIGIT;
fragment DIGIT : [0-9]+;
ID : [a-zA-Z] IDTAIL*;
fragment IDTAIL : [a-zA-Z] | [0-9];

/** Production rules - name must begin with lowercase letter */
root : '<BEGIN' statement+ 'END>' EOF;
statement : (assignment | arithmeticExpression) ';';
assignment : ID EQ arithmeticExpression;
arithmeticExpression : lOp = (INT | ID) OPERATOR rOp = (INT | ID);

ArithmeticParser.ast

RootNode ::= StatementNode*;
abstract StatementNode;
AssignmentNode:StatementNode ::= <ID> ArithmeticExpressionNode;
ArithmeticExpressionNode:StatementNode ::= lOp:operand <OPERATOR> rOp:operand;
abstract operand;
intOp:operand ::= <INT>;
idOp:operand ::= <ID>;

Классы, соответствующие типам узлов:

/**
 * Required as superclass of all nodes for AstBuilderVisitor class
 */
abstract class AstNode { }

class RootNode extends AstNode {
    // children nodes of the root node
    ArrayList<StatementNode> statementList;
    public RootNode() {
        this.statementList = new ArrayList<>();
        System.out.println("\tNEW RootNode successfully created");
    }
}

abstract class StatementNode extends AstNode { }

class AssignmentNode extends StatementNode {
    String id;
    ArithmeticExpressionNode arithmeticExpression;

    AssignmentNode(String id, ArithmeticExpressionNode arithmeticExpression) {
        this.id = id;
        this.arithmeticExpression = arithmeticExpression;
        System.out.println("\tNEW AssignmentNode: id = " + id);
    }
}

class ArithmeticExpressionNode extends StatementNode {
    char operator;
    char lOpType, rOpType;
    String lOp, rOp;

    public ArithmeticExpressionNode(char operator, char lOpType, char rOpType, String lOp, String rOp) {
        this.operator = operator;
        this.lOpType = lOpType;
        this.rOpType = rOpType;
        this.lOp = lOp;
        this.rOp = rOp;
        System.out.println("\tNEW ArithmeticExpressionNode: " + lOp + " "  + operator + " " + rOp);
    }
}

И, наконец,файл build.xml:

<!-- A project is one or more targets that executes tasks. This project
     has three targets: build, clean, and test. The build target is
     set as the default target and used if no target is supplied. -->
<project name="Compiler" default="build" basedir=".">

    <!-- The name used for the ANTLR files  -->
    <property name="parser.name" value="ArithmeticParser"/>

    <!-- The directory where generated files will be stored -->
    <property name="package" value="AST"/>

    <!-- The directory where tools like ANTLR and jastadd are stored. -->
    <property name="tools" value="tools"/>

    <!-- The JastAdd ANT task -->
    <taskdef classname="jastadd.JastAddTask" name="jastadd" classpath="${tools}/jastadd2.jar" />

    <!-- gen:
      - Creates a directory for generated files.
      - Generates the AST classes using jastadd.
      - Generates the parser using antlr. -->
    <target name="gen">
        <mkdir dir="${package}"/>
        <jastadd package="${package}" jjtree="false" grammar="${parser.name}">
            <fileset dir=".">
                <include name="**/*.ast"/>
                <include name="**/*.jrag"/>
                <include name="**/*.jadd"/>
            </fileset>
        </jastadd>
        <antlr
                target="${parser.name}.g4"
                outputdirectory="${package}"
        />
    </target>

    <!-- build: (automatically runs "gen" if needed)
      - compiles all java files
      - intended to be used from the command line
        (in Eclipse you don't need this target since Eclipse compiles
        java files automatically)
      - you can change "javac1.4" to "jikes" for faster compilation -->
    <target name="build" depends="gen">
        <javac
                compiler="javac1.4"
                srcdir="."
                classpath="${tools}/junit.jar"
        />
    </target>

    <!-- clean:
      - deletes the directory holding generated files
      - deletes all .class files (recursively) -->
    <target name="clean">
        <delete dir="${package}"/>
        <delete>
            <fileset dir="." includes="**/*.class"/>
        </delete>
    </target>
</project>

РЕДАКТИРОВАТЬ: Хорошо, я нашел способ сделать это так, чтобы в build.xml я заменил

<antlr
    target="${parser.name}.g4"
    outputdirectory="${package}"
/>

на

<java jar="tools/antlr-4.7.2-complete.jar" fork="true">
    <arg value="-o"/>
    <arg value="."/>
    <arg value="${parser.name}.g4"/>
</java>

так что теперь antlr запускается.Но теперь JastAdd не работает вообще.Я не уверен, почему это так.Было бы полезно иметь ant-antlr4.jar, если он где-то существует.Интересно, просто использование этой задачи <java> ant приводит к тому, что JastAdd по какой-то причине не запускается?

...