Go工具yacc中最简单的解析器

10

使用以下命令:

go tool yacc -p Verb -o verb.go boilerplate.y

尝试构建此yacc文件:

// boilerplate.y
%{

package main

import (
    "bufio"
    "fmt"
    "os"
    "unicode"
)

%}

%% 

.|\n   ECHO;

%%

func main() {
    fi := bufio.NewReader(os.NewFile(0, "stdin"))
  s, err := fi.ReadString('\n')
  if err != nil {
    fmt.Println('error', err)
  } 

  VerbParse(&VerbLex{s: s})
}

错误:第一条规则的语法有误:boilerplate.y:16

已成功使此示例工作:

https://github.com/golang-samples/yacc/blob/master/simple/calc.y

尝试构建自己的内容并通过lex和yacc书籍进行学习。 资源似乎非常有限或不存在。

2个回答

8

您的规格说明中存在错误的规则

一个规格说明文件具有以下声明:

declarations
%%
rules
%%
programs

规则的定义如下:

A  :  BODY  ;

其中A是非终结符号,而BODY由令牌(终结符)、非终结符和字面量组成。 :;是规则声明语法的必需组成部分。

因此,规则为:

.|\n   ECHO;

语法有误。

由于您只是试图回显输入内容,基于 calc.y 的一个非常简单的实现如下(文件 echo.y):

规则

%%

in : /* empty */
  | in input '\n'
     { fmt.Printf("Read character: %s\n", $2) }
  ;

input : CHARACTER
  | input CHARACTER
      { $$ = $1 + $2 }
  ;

程序

%%

type InputLex struct {
    // contains one complete input string (with the trailing \n)
    s string
    // used to keep track of parser position along the above imput string
    pos int
}

func (l *InputLex) Lex(lval *InputSymType) int {
    var c rune = ' '

    // skip through all the spaces, both at the ends and in between
    for c == ' ' {
        if l.pos == len(l.s) {
            return 0
        }
        c = rune(l.s[l.pos])
        l.pos += 1
    }

    // only look for input characters that are either digits or lower case
    // to do more specific parsing, you'll define more tokens and have a 
    // more complex parsing logic here, choosing which token to return
    // based on parsed input
    if unicode.IsDigit(c) || unicode.IsLower(c) {
        lval.val = string(c)
        return CHARACTER
    }

    // do not return any token in case of unrecognized grammer
    // this results in syntax error
    return int(c)
}

func (l *InputLex) Error(s string) {
    fmt.Printf("syntax error: %s\n", s)
}

func main() {
    // same as in calc.y
}

func readline(fi *bufio.Reader) (string, bool) {
    // same as in calc.y
}

要编译并运行此程序,请在命令提示符下执行以下操作:

go tool yacc -o echo.go -p Input echo.y
go run echo.go

如您所见,您将需要在 Lex 方法中定义自己的解析规则。结构体 InputLex 用于在解析输入时保存值。 InputSymType 是自动生成的,并由规范的 declaration 部分中声明的 %union 定义。

据我所知,没有直接使用 JISON 或正则表达式来使用 Go 的 yacc 工具进行匹配的方法。您可能需要查看其他库。

更多详细信息请参见此处:http://dinosaur.compilertools.net/yacc/

完整可工作的代码在此处:https://play.golang.org/p/u1QxwRKLCl


我只是想要一个基本的文件来回显标准输入。我对JISON只有一点了解,在其中有词法规则和语法规则。我希望能够使用Go在一个.y文件中将这两者分开,以便我可以重建简单的计算文件,然后可能是一个JSON解析器或其他东西。 - Justin Thomas
这里是JISON在同一文件中使用lex规则:http://zaa.ch/jison/demos/calc/。我不太明白你提供的链接中如何使用lex函数。我看到可以使用正则表达式将lex阶段分解为标记。 - Justin Thomas
添加了一个基本的 echo 解析器。据我所知,JISON 可能无法与 Go 工具中的 Yacc 一起使用。 - abhink
不过,你说对了一件事。缺乏适当的文档确实很严重。 - abhink
TIL:只需使用ANTLR :) 最近有人成功地实现了一个Go目标。 - Justin Thomas

-2

所有的 yacc 语法文件都应该有三个部分:

  • 声明
  • 规则
  • 程序

在声明中,你可以使用 %token, %type, %start,来指定语法中的 token 列表、非终结符列表和起始符号。

规则的形式是 A:Body {action codes}

程序需要实现函数 lexer 或 getToken

请参考示例 https://github.com/acekingke/yaccgo/blob/main/examples/e.y

很容易理解,只需解析字符串 "nnn..." 即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接