前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 译文之词法分析与解析 Part Three

Go 译文之词法分析与解析 Part Three

作者头像
波罗学
发布2019-08-02 11:22:15
2530
发布2019-08-02 11:22:15
举报

结构体

解析器负责启动词法器和从 channel 读取 Token 的组件。接收到 Token 后,解析器需要知道当前 Token 状态,然后将其解析到对应结构中。我们要做的第一件事就是,定义表示 INI 内容的结构体。将主要涉及三个结构体。

第一个表示 Key/Value 的结构体,名称为 IniKeyValue,如下。

model/ini/IniKeyValue.go

package ini

type IniKeyValue struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}
复制代码

第二个表示 Section 的结构体,名称为 IniSection,如下:

/model/ini/IniSection.go

package ini

type IniSection struct {
	Name          string        `json:"name"`
	KeyValuePairs []IniKeyValue `json:"keyValuePairs"`
}
复制代码

我们知道,Section 是由 Key/Value 组成的,其中 KeyValuePairs 即是属于这个 Section 的 Key/Value。如果 Key/Value 是不属于任何 Section,将会属于 Name 为空的 Section 中。

最后一个表示整个文件的结构体,名称为 IniFile,如下:

/model/ini/IniFile.go

package ini

type IniFile struct {
	FileName string       `json:"fileName"`
	Sections []IniSection `json:"sections"`
}
复制代码

IniFile 有两个成员字段组成,分别 FileName 文件名称和一系列 Section。

解析器

解析器的编写,我们要做的第一件事是,创建一个用于存放解析结构的变量,即一个 IniFile 结构体类型变量。如下:

output := ini.IniFile{
   FileName: fileName,
   Sections: make([]ini.IniSection, 0),
}
复制代码

现在,我们还需要一些变量追踪 Token、 Token 的值,还有解析器当前的状态。例如,当我们在获取 Key/Value 时,我们需要知道当前处于哪个 Section 中。接下来,就可以启动词法器了。

var token lexertoken.Token
var tokenValue string

/* State variables */
key := ""

log.Println("Starting lexer and parser for file", fileName, "...")

l := lexer.BeginLexing(filename, input)
复制代码

到此,相关变量已经定义完成,并且词法器也成功启动。接下来开始从 channel 接收 Token,如果 Token 类型不是 TOKEN_VALUE,执行 Trim 空格。

for {
	token = l.NextToken()

	if token.Type != lexertoken.TOKEN_VALUE {
		tokenValue = strings.TrimSpace(token.Value)
	} else {
		tokenValue = token.Value
	}

	...
复制代码

我们知道,如果词法器遍历到文件结尾,将返回类型为 EOF 的 Token。此时,需要将 Section 和 Key/Value 记录下来,并退出循环。

if isEOF(token) {
	output.Sections = append(output.Sections, section)
	break
}
复制代码

parser 函数的最后实现具体 Token 的处理,主要有三个 Token 需要关注。

第一个是 Section Token,如果遇到 Section Token,我们首先检查 section 变量中是否已存在 Key/Value,有则将它记录到 output.Sections 中。然后重置 section 变量追踪当前 Section 和接下来 Key/Value。

接着是 Key/Value。如果遇到 TOKEN_KEY,变量 key 用于记录 TOKEN_KEY 的值。遇到 TOKEN_VALUE,我们就可以变量 key 对应的 value,并将其 append 到 section.KeyValuePairs 中。

示例代码如下:

switch token.Type {
   case lexertoken.TOKEN_SECTION:
      /*
* Reset tracking variables
*/
      if len(section.KeyValuePairs) > 0 {
         output.Sections = append(output.Sections, section)
      }

      key = ""

      section.Name = tokenValue
      section.KeyValuePairs = make([]ini.IniKeyValue, 0)

   case lexertoken.TOKEN_KEY:
      key = tokenValue

   case lexertoken.TOKEN_VALUE:
      section.KeyValuePairs = append(section.KeyValuePairs, ini.IniKeyValue{
         Key: key,
         Value: tokenValue,
      })
      key = ""
   }
复制代码

测试

开发工作已经完成,下面进入测试阶段。Github 上有相应的测试代码,go get 下载好代码,在你的 GOPATH 的 src/github.com/adampresley/sample-ini-parser 目录下的 sampleIniParser.go 即是测试代码。

代码如下:

sampleInput := `
key=abcdefg

[User]
userName=adampresley
keyFile=~/path/to/keyfile

[Servers]
server1=localhost:8080
`

parsedINIFile := parser.Parse("sample.ini", sampleInput)
prettyJSON, err := json.MarshalIndent(parsedINIFile, "", " ")

if err != nil {
   log.Println("Error marshalling JSON:", err.Error())
   return
}

log.Println(string(prettyJSON))
复制代码

直接通过 go run sampleIniParser.go 执行即可。执行输出如下:

2019/08/01 00:06:33 Starting lexer and parser for file sample.ini ...
2019/08/01 00:06:33 Parser has been shutdown
2019/08/01 00:06:33 {
   "fileName": "sample.ini",
   "sections": [
      {
         "name": "",
         "keyValuePairs": [
            {
               "key": "key",
               "value": "abcdefg"
            }
         ]
      },
      {
         "name": "User",
         "keyValuePairs": [
            {
               "key": "userName",
               "value": "adampresley"
            },
            {
               "key": "keyFile",
               "value": "~/path/to/keyfile"
            }
         ]
      },
      {
         "name": "Servers",
         "keyValuePairs": [
            {
               "key": "server1",
               "value": "localhost:8080"
            }
         ]
      }
   ]
}
复制代码

总结

这个系列文章是非常有挑战性,但也非常有趣。词法分析与解析是一个非常复杂的话题,有太多内容需要学习。我们可以看到,即使像上面 INI 文件解析这样简单的工作,我们也需要花费一些精力才能完成。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年08月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 解析器
  • 测试
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档