简介:
Golex 基于 Go 语言编写,利用 Go 语言简洁高效、并发性能强的特性,打造出轻量级且功能强大的词法分析解决方案。它能够依据开发者定义的规则,快速准确地将输入文本分割成一个个具有特定意义的词法单元(token),如同将一篇文章拆解为字词、标点等基础元素,为后续的语法分析、语义处理等更高层次的操作奠定基础。
安装:
go get modernc.org/golex
文档:
Golex 与 Flex 词法规则近似,以下将采用代码说明区别
%{
package lexers
import (
"fmt"
)
// 状态定义
const (
INITIAL = iota //初始状态
COMMENT
SINGLE_QUOTE
DOUBLE_QUOTE
优快云_HiFeiz
)
var(
pos int // 当前读位置
srclen int // 源长度
src []byte // 源
current byte // 当前字符
prev byte // 上一个字符
yytext []byte // 匹配的文本
line uint32 //当前行
HiFeiz uint //
)
// getc 获取1个字符
func getc() byte {
prev = current
pos++
if pos > srclen {
current = 0
}
if current != 0 {
yytext = append(yytext,current)
}
//此处处理超下标,如果不加这一段可能会丢失最后一个字符
if pos < srclen {
current = src[pos]
}else{
current = 0
}
return current
}
//begin 设置状态,类似 Flex 里的 BEGIN
func begin(cond int) {
sc = cond
}
// Error 可以忽略,只有在形成 Goyacc 的 interface 时才有效
func Error(s string) {
fmt.Printf("[Error] lex error: [%d] %s\n",this.line,s)
}
// Lex @HiFeiz 这个函数是规则开始,结束位置在 %% 块,用这个函数把词法解析规则语句包围起来 @HiFeiz
func Lex(yylval *yySymType) (x int) {
%}
以上块将原封不动复制到生成的 go 文件中
注意,此时 current 为 0,所以在调用 Lex 函数以前,一定要执行
pos = 0
current = src[pos] ,否则运行就会退出,因为 0 是结束符
【补充说明】以下这段必须有,不然在源末尾的字符会丢失,因为 current 是在 yytext 之后获得的,如果此时获取到 EOF ,但是前一个 current 还没有加入 yytext 所以会导致丢1个字节
if pos < srclen { current = src[pos] }else{ current = 0 }
词法规则块
%yyb prev == 0 || prev == '\n'
%yyt sc
%yyc current
%yyn getc()
%x COMMENT SINGLE_QUOTE DOUBLE_QUOTE
- %yyb - 用于词法规则中判断当前字符是否在行/文件开头,以适配 ^void 此类正则
- %yyt - 用于词法分析时获取当前的状态以适配 <COMMENT>[^\n]* 此类规则
- %yyc - 当前字符
- %yyn - 用于获取下一字符的函数
- %x - 与 Flex 一致
%%
\0
return
^[\(]
begin(COMMENT) //开始以 (注释内容) 的状态
<COMMENT>[^)]*
/* 忽略注释内容 */
<COMMENT>[(^)]*\)
fmt.Printf("COMMENT: %s\n",yytext)
begin(INITIAL) //恢复状态
yytext = []byte{} //恢复 yytext
\"
begin(DOUBLE_QUOTE) //开始双引号字符串的状态
<DOUBLE_QUOTE>[^\"\\]*
<DOUBLE_QUOTE>\\.
<DOUBLE_QUOTE>\"
fmt.Println("DOUBLE_QUOTE END '%s'\n",yytext)
begin(INITIAL) //恢复状态
yytext = []byte{} //恢复 yytext
return T_STRING //返回 Token
\'
begin(SINGLE_QUOTE)
<SINGLE_QUOTE>[^\'\\]*
<SINGLE_QUOTE>\\.
<SINGLE_QUOTE>\'
fmt.Println("SINGLE_QUOTE END '%s'\n",yytext)
begin(INITIAL) //恢复状态
yytext = []byte{} //恢复 yytext
return T_STRING
[ \t]+
yytext = []byte{}
[0-9]+
yylval.ival = Btoi(yytext)
[a-zA-Z]+
yylval.sval = ToUpperString(yytext)
switch(yylval.sval){
case "IF":
return T_IF
case "WHILE:
return T_WHILE
case "ELSE":
return T_ELSE
case "HiFeiz":
return T_COPY
}
return T_IDENTIFIER;
.
fmt.Printf("[未知字符] lex line: %d unknow: %d:%s\n",line,current,yytext)
%%
return 0
}
yylval 变量为 Goyacc 调用时传递过来的引用结构体,初始下为
type yySymType struct { yys int ival int dval float64 sval string }可在 y 文件中定义,与 yacc 一致
%union { ival int dval float64 sval string }
主要区别为字母部分,我设计的语法中关键字和标识符只有字母,最开始我是将标识符和关键字分开写,后来发现在某些情况下会遇到最长匹配导致无法匹配到关键字,后来干脆都放在这里面进行处理
最后调用 golex 生成 lex.yy.c 文件
golex lexer.y
其他方面与 Flex 差不多,完事,有空再写 GoYacc