我已经搜索了很多关于这一点,我没有找到任何有用的,真正帮助我建立一个AST。我已经知道ANTLR4不像ANTLR3以前那样构建AST。每个人都说:“嘿,使用访客!”,但我找不到任何例子或更详细的解释,说明我该如何做到这一点...
我有一个语法必须像C,但与每一个命令写葡萄牙语(葡萄牙编程语言)。我可以使用ANTLR4轻松地生成解析树。我的问题是:我现在需要做什么来创建一个AST?
编辑2:现在我有一个类来创建DOT文件,我只需要弄清楚如何正确使用访问者
好的,让我们构建一个简单的数学例子。构建一个AST对于这样的任务来说完全是矫枉过正的,但这是一个展示原理的好方法。
我将在C#中实现,但Java版本将非常类似。
首先,让我们写一个非常基本的数学语法来使用:
grammar Math;
compileUnit
: expr EOF
;
expr
: '(' expr ')' # parensExpr
| op=('+'|'-') expr # unaryExpr
| left=expr op=('*'|'/') right=expr # infixExpr
| left=expr op=('+'|'-') right=expr # infixExpr
| func=ID '(' expr ')' # funcExpr
| value=NUM # numberExpr
;
OP_ADD: '+';
OP_SUB: '-';
OP_MUL: '*';
OP_DIV: '/';
NUM : [0-9]+ ('.' [0-9]+)? ([eE] [+-]? [0-9]+)?;
ID : [a-zA-Z]+;
WS : [ \t\r\n] -> channel(HIDDEN);
internal abstract class ExpressionNode
{
}
internal abstract class InfixExpressionNode : ExpressionNode
{
public ExpressionNode Left { get; set; }
public ExpressionNode Right { get; set; }
}
internal class AdditionNode : InfixExpressionNode
{
}
internal class SubtractionNode : InfixExpressionNode
{
}
internal class MultiplicationNode : InfixExpressionNode
{
}
internal class DivisionNode : InfixExpressionNode
{
}
internal class NegateNode : ExpressionNode
{
public ExpressionNode InnerNode { get; set; }
}
internal class FunctionNode : ExpressionNode
{
public Func<double, double> Function { get; set; }
public ExpressionNode Argument { get; set; }
}
internal class NumberNode : ExpressionNode
{
public double Value { get; set; }
}
这很容易通过访问者完成,ANTLR为我们提供了一个MathBaseVisitor
类,因此让我们使用它。
internal class BuildAstVisitor : MathBaseVisitor<ExpressionNode>
{
public override ExpressionNode VisitCompileUnit(MathParser.CompileUnitContext context)
{
return Visit(context.expr());
}
public override ExpressionNode VisitNumberExpr(MathParser.NumberExprContext context)
{
return new NumberNode
{
Value = double.Parse(context.value.Text, NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent)
};
}
public override ExpressionNode VisitParensExpr(MathParser.ParensExprContext context)
{
return Visit(context.expr());
}
public override ExpressionNode VisitInfixExpr(MathParser.InfixExprContext context)
{
InfixExpressionNode node;
switch (context.op.Type)
{
case MathLexer.OP_ADD:
node = new AdditionNode();
break;
case MathLexer.OP_SUB:
node = new SubtractionNode();
break;
case MathLexer.OP_MUL:
node = new MultiplicationNode();
break;
case MathLexer.OP_DIV:
node = new DivisionNode();
break;
default:
throw new NotSupportedException();
}
node.Left = Visit(context.left);
node.Right = Visit(context.right);
return node;
}
public override ExpressionNode VisitUnaryExpr(MathParser.UnaryExprContext context)
{
switch (context.op.Type)
{
case MathLexer.OP_ADD:
return Visit(context.expr());
case MathLexer.OP_SUB:
return new NegateNode
{
InnerNode = Visit(context.expr())
};
default:
throw new NotSupportedException();
}
}
public override ExpressionNode VisitFuncExpr(MathParser.FuncExprContext context)
{
var functionName = context.func.Text;
var func = typeof(Math)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.ReturnType == typeof(double))
.Where(m => m.GetParameters().Select(p => p.ParameterType).SequenceEqual(new[] { typeof(double) }))
.FirstOrDefault(m => m.Name.Equals(functionName, StringComparison.OrdinalIgnoreCase));
if (func == null)
throw new NotSupportedException(string.Format("Function {0} is not supported", functionName));
return new FunctionNode
{
Function = (Func<double, double>)func.CreateDelegate(typeof(Func<double, double>)),
Argument = Visit(context.expr())
};
}
}
正如您所看到的,这只是通过使用访问者从CST节点创建一个AST节点的问题。代码应该是非常简单的(嗯,可能除了visitfuncexpr
的东西,但它只是将委托连接到System.Math
类的适当方法的一种快速方法)。
这里有AST的建筑材料。这就是所需要的。只需从CST中提取相关信息,并将其保留在AST中即可。
现在,让我们玩一点AST。我们必须构建一个AST访问者基类来遍历它。让我们只做一些类似于antlr提供的abstractParsetreevisitor
的事情。
internal abstract class AstVisitor<T>
{
public abstract T Visit(AdditionNode node);
public abstract T Visit(SubtractionNode node);
public abstract T Visit(MultiplicationNode node);
public abstract T Visit(DivisionNode node);
public abstract T Visit(NegateNode node);
public abstract T Visit(FunctionNode node);
public abstract T Visit(NumberNode node);
public T Visit(ExpressionNode node)
{
return Visit((dynamic)node);
}
}
在这里,我利用C#的dynamic
关键字在一行代码中执行了双重分派。在Java中,您必须自己使用一系列if
语句进行连接,这些语句如下所示:
if (node is AdditionNode) {
return Visit((AdditionNode)node);
} else if (node is SubtractionNode) {
return Visit((SubtractionNode)node);
} else if ...
但我只是想找到这个例子的捷径。
internal class EvaluateExpressionVisitor : AstVisitor<double>
{
public override double Visit(AdditionNode node)
{
return Visit(node.Left) + Visit(node.Right);
}
public override double Visit(SubtractionNode node)
{
return Visit(node.Left) - Visit(node.Right);
}
public override double Visit(MultiplicationNode node)
{
return Visit(node.Left) * Visit(node.Right);
}
public override double Visit(DivisionNode node)
{
return Visit(node.Left) / Visit(node.Right);
}
public override double Visit(NegateNode node)
{
return -Visit(node.InnerNode);
}
public override double Visit(FunctionNode node)
{
return node.Function(Visit(node.Argument));
}
public override double Visit(NumberNode node)
{
return node.Value;
}
}
internal class Program
{
private static void Main()
{
while (true)
{
Console.Write("> ");
var exprText = Console.ReadLine();
if (string.IsNullOrWhiteSpace(exprText))
break;
var inputStream = new AntlrInputStream(new StringReader(exprText));
var lexer = new MathLexer(inputStream);
var tokenStream = new CommonTokenStream(lexer);
var parser = new MathParser(tokenStream);
try
{
var cst = parser.compileUnit();
var ast = new BuildAstVisitor().VisitCompileUnit(cst);
var value = new EvaluateExpressionVisitor().Visit(ast);
Console.WriteLine("= {0}", value);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine();
}
}
}
现在我们终于可以玩它了:
问题内容: 我一直在搜索有关此的内容,但找不到任何能真正帮助我构建AST的有用信息。我已经知道ANTLR4不会像以前的ANTLR3那样构建AST。每个人都说:“嘿,请使用访客!”,但是我找不到任何示例或有关如何执行此操作的更详细的说明… 我有一个像C一样的语法,但是每个命令都是用葡萄牙语(葡萄牙语编程语言)编写的。我可以使用ANTLR4轻松生成解析树。我的问题是:创建AST现在需要做什么? 顺便说
我有一个ANTLR3语法,它构建了一个抽象语法树。我正在考虑升级到ANTLR4。但是,ANTLR4似乎只构建解析树,而不构建抽象语法树。例如,选项不再被识别。此外,在“最终的ANTLR4参考文献”的文本中既没有“AST”也没有“抽象语法”。
我试图使用ANTLR4在golang创建一个javascript解析器。我使用的语法是这样的(https://github.com/antlr/grammars-v4/tree/master/javascript/ecmascript),我遵循自述文件https://github.com/antlr/antlr4/blob/master/doc/go-target.md中的说明 因此,我从语法中生
我正试图弄清楚如何使用ANTLR,但我很难消化我所发现的东西。到目前为止,以下是我的资源: 如何使用ANTLR4创建AST? 如何使用Gradle 2.10将一个ANTLR lexer语法导入到另一个语法中? https://github.com/antlr/grammars-v4/tree/master/java8 https://dzone.com/articles/parsing-any-l
登录企业管理,轻应用-创建轻应用 设置轻应用头像、名称、开发者权限、设置管理员,创建轻应用
我想知道我们是否可以使用Antlr版本4构建一个AST。我找不到任何关于使用ANTLR4构建它的参考。一个答案是,使用antlr4会很容易,它只产生解析树,但我的问题是效率如何? 它迫使我们只能爬取整个解析树而不是抽象的语法树,这对于遍历整个解析树和使用访问者执行任务是不有效的。