【问题标题】:Create mandatory token in ANTLR在 ANTLR 中创建强制令牌
【发布时间】:2020-07-30 10:56:52
【问题描述】:

我刚刚进入 ANTLR。我正在尝试创建一个简单的 hello world ANTLR。我的目标是使“Hello world”成为强制性字符串。因此,我期望“Hello”的输入被认为是无效的,并且它给了我一个错误,说明它需要一个“世界”令牌。

编辑:请注意,我确实希望“hello”和“world”成为单独的标记(将它们视为关键字),以便我可以轻松地分别识别它们。

我有下面的helloworld.g4:

grammar helloworld;

WHITESPACE: [ \r\n\t]+ -> skip;
HELLO : 'Hello' ;
WORLD : 'world' ;

start : HELLO WORLD EOF ;

我有以下 main.go:

package main

import (
    "fmt"
    "test/parser"

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

const rule = `Hello`

type testListener struct {
    *parser.BasehelloworldListener
}

func main() {
    // Setup the input
    is := antlr.NewInputStream(rule)

    // Create the Lexer
    lexer := parser.NewhelloworldLexer(is)
    // Read all tokens

    for {
        t := lexer.NextToken()
        if t.GetTokenType() == antlr.TokenEOF {
            break
        }
        fmt.Printf("%s (%q)\n",
            lexer.SymbolicNames[t.GetTokenType()],
            t.GetText())
    }

    // Finally parse the expression
    stream := antlr.NewCommonTokenStream(lexer,
        antlr.TokenDefaultChannel)

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

    // Finally parse the expression
    antlr.ParseTreeWalkerDefault.Walk(&testListener{}, p.Start())
}

我正在构建一个 Go 解析器,并使用以下命令测试结果:

antlr -Dlanguage=Go -o parser helloworld.g4 && go run main.go

哪些输出:

HELLO ("Hello")
line 1:5 mismatched input '<EOF>' expecting 'Hello'

我想知道我可以做些什么来给我一个输出,说明“世界”是“你好”之后的预期标记。它不应该期待另一个“Hello”,它应该期待“world”,然后是一个 EOF。

【问题讨论】:

    标签: go antlr antlr4


    【解决方案1】:

    在词法分析器中,您定义了 2 个单独的标记,因此词法分析器对输入 "Hello" 没有任何问题。

    如果该 hello-token 应该始终跟在 "world" 之后,那么您必须在令牌中包含它:

    HELLO : 'Hello' ' '+ 'world';
    

    如果您调用解析器规则start,那导致错误。这通常是强制存在WORLD 令牌(在解析器中)的方式,而不是在词法分析器中。

    编辑

    您正在使用所有标记,然后将这个“已使用的词法分析器”提供给解析器。跳过标记的打印,或在打印标记后重新初始化词法分析器。

    应该这样做:

    func main() {
        is := antlr.NewInputStream(`Hello`)
    
        lexer := parser.NewhelloworldLexer(is)
    
        stream := antlr.NewCommonTokenStream(lexer,
            antlr.TokenDefaultChannel)
    
        p := parser.NewhelloworldParser(stream)
    
        antlr.ParseTreeWalkerDefault.Walk(&testListener{}, p.Start())
    }
    

    【讨论】:

    • 嘿,谢谢。也许我应该在我的问题中进一步澄清一下:我确实希望“hello”和“world”保持独立的标记,这样我就可以轻松地分别识别它们。
    • 我的答案仍然是一样的:当您将它们作为单独的标记时,您无法在词法分析器内部知道缺少WORLD 标记。词法分析器也不应该对此负责:那是解析器的工作(它已经做了)。如果你解析(所以不仅仅是标记化!)"Hello",你会看到一个错误出现。
    • 啊,这解释了我缺少什么部分。我在 main.go 中所做的纯粹是对字符串进行标记(识别找到的标记),而解析器负责确保正确放置标记。我已经用我的新 main.go 更新了这个问题,并稍微改变了语法文件,包括更新的问题。也许你可以指出我现在做错了什么。快到了。
    • 您在词法分析后调用解析器,这会将词法分析器和输入流留在文件末尾以供解析器使用。如果您需要打印出令牌流,请在调用解析器后执行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 1970-01-01
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多