Pregunta

Hay alguien consciente de tutoriales para caminar AST-antlr generados en C #? Lo más cerca que pude encontrar es este , pero no es terriblemente útil.

Mi objetivo es caminar a través de los árboles que yo estoy generando basado en un lenguaje específico del dominio que yo estoy trabajando, y para utilizar los árboles de salida generada código C #.

Un tutorial basado en Java sería de gran ayuda, también - lo que proporciona un claro ejemplo de cómo atravesar AST ANTLR

.
¿Fue útil?

Solución

he conseguido resolver esto mediante la adaptación del ejemplo al final de Manuel Abadia artículo .

Aquí está mi versión, que da la casualidad que usar para convertir código analizada a C #. Estos son los pasos:

  1. ANTLRStringStream o subclase con su entrada (que puede ser un archivo o cadena).
  2. instancia su analizador léxico generado, que pasa en esa corriente cadena.
  3. instancia de una corriente de contadores con el analizador léxico.
  4. instancia su analizador con esa cadena de componentes léxicos.
  5. Obtener el valor de nivel superior de su analizador, y convertirlo en un CommonTree.
  6. recorrer el árbol:

Para obtener el texto literal de un nodo, node.Text uso. Para obtener el nombre del token de un nodo, node.Token.Text uso.

Tenga en cuenta que node.Token.Text sólo le dará el nombre real de su token de si se trata de un token imaginaria sin cadena correspondiente. Si se trata de una muestra real, entonces node.Token.Text volverá a su cadena.

Por ejemplo, si usted tenía lo siguiente en su gramática:

tokens { PROGRAM, FUNCDEC }

EQUALS : '==';
ASSIGN : '=';

Luego le ponen "PROGRAM", "FUNCDEC", "==" y "=" de los accesos correspondientes de node.Token.Text.

Se puede ver parte de mi ejemplo a continuación, o puede navegar por la versión completa .


public static string Convert(string input)
{
    ANTLRStringStream sStream = new ANTLRStringStream(input);
    MyGrammarLexer lexer = new MyGrammarLexer(sStream);

    CommonTokenStream tStream = new CommonTokenStream(lexer);

    MyGrammarParser parser = new MyGrammarParser (tStream);
    MyGrammarParser.program_return parserResult = parser.program();

    CommonTree ast = (CommonTree)parserResult.Tree;

    Print(ast);
    string output = header + body + footer;

    return output;
}

public static void PrintChildren(CT ast)
{
    PrintChildren(ast, " ", true);
}

public static void PrintChildren(CT ast, string delim, bool final)
{
    if (ast.Children == null)
    {
        return;
    }

    int num = ast.Children.Count;

    for (int i = 0; i < num; ++i)
    {
        CT d = (CT)(ast.Children[i]);
        Print(d);
        if (final || i < num - 1)
        {
            body += delim;
        }
    }
}

public static void Print(CommonTree ast)
{
    switch (ast.Token.Text)
    {
        case "PROGRAM":
            //body += header;
            PrintChildren(ast);
            //body += footer;
            break;
        case "GLOBALS":
            body += "\r\n\r\n// GLOBALS\r\n";
            PrintChildren(ast);
            break;
        case "GLOBAL":
            body += "public static ";
            PrintChildren(ast);
            body += ";\r\n";
            break;

      ....
    }
}

Otros consejos

Normalmente caminas AST con la recursividad, y llevar a cabo diferentes acciones en función del tipo de nodo. Si está utilizando los nodos del árbol polimórficos (es decir, diferentes subclases para diferentes nodos en el árbol), haga doble despacho en el patrón del visitante puede ser apropiado; Sin embargo, eso no es por lo general muy conveniente con Antlr.

En pseudocódigo, caminar por lo general se ve algo como esto:

func processTree(t)
    case t.Type of
        FOO: processFoo t
        BAR: processBar t
    end

// a post-order process
func processFoo(foo)
    // visit children
    for (i = 0; i < foo.ChildCount; ++i)
        processTree(foo.GetChild(i))
    // visit node
    do_stuff(foo.getText())

// a pre-order process
func processBoo(bar)
    // visit node
    do_stuff(bar.getText())
    // visit children
    for (i = 0; i < foo.ChildCount; ++i)
        processTree(foo.GetChild(i))

Los tipos de procesamiento dependen de la semántica del lenguaje altamente. Por ejemplo, el manejo de una declaración IF, con la estructura (IF <predicate> <if-true> [<if-false>]), cuando se genera código para una máquina de pila como la JVM o CLR, puede tener un aspecto un poco como esto:

func processIf(n)
    predicate = n.GetChild(0)
    processExpr(predicate) // get predicate value on stack
    falseLabel = createLabel()
    genCode(JUMP_IF_FALSE, falseLabel) // JUMP_IF_FALSE is called brfalse in CLR,
                                       // ifeq in JVM
    if_true = n.GetChild(1)
    processStmt(if_true)
    if_false = n.ChildCount > 2 ? n.GetChild(2) : null
    if (if_false != null)
        doneLabel = createLabel()
        genCode(JUMP, doneLabel)
    markLabel(falseLabel)
    if (if_false != null)
        processStmt(if_false) // if-false branch
        markLabel(doneLabel)

En general todo se hace de forma recursiva en función del tipo del nodo actual, etc.

Usted debe mirar en escribir un TreeParser; que puede hacer el trabajo de interpretar el árbol mucho más simple.

Para 2.x ANTLR ver http://www.antlr2.org/doc/sor .html Para ver 3.x ANTLR http://www.antlr.org/wiki/ display / ANTLR3 / árbol + construcción (basado en Java analizador y analizador árbol ejemplo)

he hecho algo similar (pero no realmente) y terminé con un TreeParser.

También sugiero comprar el libro antlr. He encontrado que es más valioso que cualquier recurso web. Puede que no tenga todas las respuestas, pero seguro que ayuda con los conceptos básicos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top