# #go #parsing #antlr #antlr4
Вопрос:
Я слежу https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/, и
я смотрю на https://github.com/bramp/goadvent-antlr/blob/master/example2.go, у которого нет выхода. Сейчас,
Я хочу использовать EnterEveryRule
для вывода каждого встречающегося символа с их символическими именами. Как я могу это сделать?
Т. е., как каким-то образом получить доступ к symbolicNames
срезу на любом конкретном этапе, чтобы знать, с каким символическим именем я имею дело.
Комментарии:
1. CalcParser является подклассом из синтаксического анализатора, который является подклассом из распознавателя , содержащего GetSymbolicNames() . Вызов
p.GetSymbolicNames()
, где p-структура калькулятора.2. Спасибо @kaby76. Я считаю , что мне все еще нужно использовать
antlr.ParseTreeWalkerDefault.Walk
, но изнутри , предположим, я каким-то образом использовалGetSymbolicNames()
срез..."OPP", "CLP", "MUL", "DIV", "ADD", "SUB", "NUMBER", "WHITESPACE"
, как бы я узнал на определенном этапе, с каким символическим именем я имею дело? Вот вопрос, который я хочу решить. Тх!3. ParserRuleContext дерева синтаксического анализа может быть либо внутренними узлами, либо конечными узлами. Узел должен быть приведен к конечному узлу (возможно, что-то вроде
t, ok := ctx.(TerminalNode)
). С помощью указателя на TerminalNode вы можете получить тип токена (t.GetSymbol().GetTokenType()
), который представляет собой целое число, используемое в массиве, возвращаемом GetSymbolicNames().4. Может быть, что-то вроде этого:
func (l *calcListener) EnterEveryRule(ctx antlr.ParserRuleContext) { t, ok := ctx.(antlr.TerminalNode); if ok { x := t.GetSymbol().GetTokenType(); y := l.parser.GetSymbolicNames(); fmt.Println(y[x]); } }
за вычетом правильного форматирования Go, удаления точек с запятой и т. Д. И добавленияparser antlr.Parser
в структуру calcListener.5. Вау, я бы никогда не смог придумать такое решение сам. Не могли бы вы ответить на вопрос вместо @kaby76, пожалуйста? Я все приготовил для тебя в play.golang.org/p/dXFgk4FPGfQ . До сих пор кажется , что для самого
t, ok :=
,ok
всегда будет ложным.
Ответ №1:
Я, конечно, не эксперт в Go, но этот пример содержит следующие строки:
// Finally parse the expression (by walking the tree)
antlr.ParseTreeWalkerDefault.Walk(amp;calcListener{}, p.Start())
Это немного вводит в заблуждение. p.Start()
Часть этого оператора фактически выполняет синтаксический анализ (вызов p.Start()
должен возвращать дерево синтаксического анализа ANTLR. На этом этапе вы выполнили синтаксический анализ.
antlr.ParseTreeWalkerDefault.Walk()
это то, как вы проходите по дереву синтаксического анализа, вызывая методы listener
, которые передаются вместе с деревом синтаксического анализа, полученным в результате p.start()
вызова. Возможно, вы захотите сохранить его во временной переменной, прежде чем передавать ее ходоку (возможно, проверьте его в отладчике, чтобы понять это, или в нем должно быть что-то вроде toStringTree(...)
функции, которая даст вам строковое представление дерева синтаксического анализа).
похоже, что calcListener
просто есть ссылка на а parser.BaseCalcListener
. Сгенерированные Base*Listener
ANTLR s являются слушателями «ничего не делать», которые просто предоставляют пустые реализации всех методов, которые должен был реализовать слушатель.
Вам нужно будет что-то сделать с calcListener
функцией, чтобы «переопределить» EnterEveryRule
функцию, и что-то сделать с *Context
переданным ей объектом.
Комментарии:
1. Спасибо! Что касается последнего шага, «сделайте что-нибудь с объектом контекста*» , Вот где я застрял и не знаю, как перейти от
*Context
объектаantlr.ParserRuleContext
к токенам (NUMBER
,ADD
и т. Д.), С которыми я Имею дело в этом конкретном дереве синтаксического анализа.2. Я не программист Go, поэтому я не знаком с целью Go ANTLR. Судя по комментариям, похоже, что @kaby76 затронул эти детали.
3. Спасибо! Начинайте голосовать!
Ответ №2:
На самом деле я отвечаю за @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() {
// 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(amp;listener, p.Start())
}
Результат выполнения:
NUMBER 1
NUMBER 2
NUMBER 3
MUL 2*3
ADD 1 2*3