ANTLR在遍历解析树时获取符号名称的方法

huangapple go评论93阅读模式
英文:

ANTLR to get symbolicNames when walking the parser tree

问题

我正在按照https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/的指导进行操作,并且我正在查看https://github.com/bramp/goadvent-antlr/blob/master/example2.go,但是它没有输出。现在,我想使用EnterEveryRule来输出遇到的每个符号及其符号名称。我该如何做到这一点?

也就是说,如何在任何特定阶段访问symbolicNames切片,以了解我正在处理哪个符号名称。

英文:

I'm following https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/, and
I'm looking at https://github.com/bramp/goadvent-antlr/blob/master/example2.go, which has no output. Now,

I want to use the EnterEveryRule to output each symbol encountered, with their symbolicNames. How can I do that?

I.e., how to somehow access to the symbolicNames slice at any specific stage, to know which SymbolicName I'm dealing with.

答案1

得分: 1

我对Go并不是很精通,但是这个例子包含了以下几行代码:

	// 最后解析表达式(通过遍历树)
	antlr.ParseTreeWalkerDefault.Walk(&calcListener{}, p.Start())

这有点误导人。这个语句中的p.Start()实际上是进行解析的(调用p.Start()应该会返回一个ANTLR解析树)。在这一点上,你已经完成了解析。

antlr.ParseTreeWalkerDefault.Walk()是用来遍历解析树并在传递给它的listener上调用方法的。你可能想将它存储在一个临时变量中,然后将其传递给遍历器(可以在调试器中检查它,或者它应该有一个类似toStringTree(...)的函数,可以给你一个解析树的字符串表示)。

看起来calcListener只是一个对parser.BaseCalcListener的引用。ANTLR生成的Base*Listener是“空实现”监听器,它只提供了所有监听器需要实现的方法的空实现。

你需要对calcListener做一些处理,以“覆盖”EnterEveryRule函数,并对传递给它的*Context对象进行一些操作。

英文:

I'm certainly no expert with Go, but that example contains the following lines:

	// Finally parse the expression (by walking the tree)
	antlr.ParseTreeWalkerDefault.Walk(&calcListener{}, p.Start())

This is a bit misleading. The p.Start() part of that statement actually does the parsing (calling p.Start() should return an ANTLR parse tree. At that point, you've done the parsing.

antlr.ParseTreeWalkerDefault.Walk() is how you walk a parse tree invoking methods on the listener that is passed in along with the parse Tree you got from the p.start() call. You may want to store it in a temporary variable before passing it to the walker (maybe inspect it in a debugger to understand it, or, it should have something like a toStringTree(...) function that will give you a string representation of the parse tree.)

it appears that calcListener just has a reference to a parser.BaseCalcListener. The ANTLR-generated Base*Listeners are "do-nothing" listeners that just provide empty implementations of all the methods a listener needs to have implemented.

You'll need to do something with the calcListener to "override" the EnterEveryRule function, and do something with the *Context object passed to it.

答案2

得分: 1

我正在为**@kaby76**回答,他引导我到了https://play.golang.org/p/R8Sik7sdZaz,所以我能够继续前进到https://play.golang.org/p/uZqfUhHE0mt。

以下是完整的代码:

package main

import (
	"fmt"

	"github.com/antlr/antlr4/runtime/Go/antlr"

	"github.com/bramp/goadvent-antlr/parser"
)

type calcListener struct {
	*parser.BaseCalcListener
	parser *parser.CalcParser
}

// ExitEveryRule is called when any rule is exited.
func (l *calcListener) ExitEveryRule(ctx antlr.ParserRuleContext) {
	count := ctx.GetChildCount()
	// count == 1, NUMBER
	ch := ctx.GetChild(0)
	if count == 3 {
		// operation
		ch = ctx.GetChild(1)
	}
	q := ch.(antlr.Tree)
	t, ok := q.(antlr.TerminalNode)
	if ok {
		s := t.GetSymbol()
		x := s.GetTokenType()
		y := l.parser.GetSymbolicNames()
		fmt.Println(y[x], ctx.GetText())
	}
}

func main() {
	// 设置输入
	is := antlr.NewInputStream("1 + 2 * 3")

	// 创建词法分析器
	lexer := parser.NewCalcLexer(is)
	stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)

	// 创建语法分析器
	p := parser.NewCalcParser(stream)

	// 最后解析表达式(通过遍历树)
	listener := calcListener{}
	listener.parser = p
	antlr.ParseTreeWalkerDefault.Walk(&listener, p.Start())
}

执行结果:

NUMBER 1
NUMBER 2
NUMBER 3
MUL 2*3
ADD 1+2*3
英文:

I'm answering it for @kaby76 actually, who had led me to https://play.golang.org/p/R8Sik7sdZaz, so that I am able to move on to
https://play.golang.org/p/uZqfUhHE0mt

Here is the full code:

package main

import (
	"fmt"

	"github.com/antlr/antlr4/runtime/Go/antlr"

	"github.com/bramp/goadvent-antlr/parser"
)

type calcListener struct {
	*parser.BaseCalcListener
	parser *parser.CalcParser
}

// ExitEveryRule is called when any rule is exited.
func (l *calcListener) ExitEveryRule(ctx antlr.ParserRuleContext) {
	count := ctx.GetChildCount()
	// count == 1, NUMBER
	ch := ctx.GetChild(0)
	if count == 3 {
		// operation
		ch = ctx.GetChild(1)
	}
	q := ch.(antlr.Tree)
	t, ok := q.(antlr.TerminalNode)
	if ok {
		s := t.GetSymbol()
		x := s.GetTokenType()
		y := l.parser.GetSymbolicNames()
		fmt.Println(y[x], ctx.GetText())
	}
}

func main() {
	// Setup the input
	is := antlr.NewInputStream("1 + 2 * 3")

	// Create the Lexer
	lexer := parser.NewCalcLexer(is)
	stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)

	// Create the Parser
	p := parser.NewCalcParser(stream)

	// Finally parse the expression (by walking the tree)
	listener := calcListener{}
	listener.parser = p
	antlr.ParseTreeWalkerDefault.Walk(&listener, p.Start())
}

Execution result:

NUMBER 1
NUMBER 2
NUMBER 3
MUL 2*3
ADD 1+2*3

huangapple
  • 本文由 发表于 2021年8月13日 01:23:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/68761815.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定