GoLex 初体验

简介:

       Golex 基于 Go 语言编写,利用 Go 语言简洁高效、并发性能强的特性,打造出轻量级且功能强大的词法分析解决方案。它能够依据开发者定义的规则,快速准确地将输入文本分割成一个个具有特定意义的词法单元(token),如同将一篇文章拆解为字词、标点等基础元素,为后续的语法分析、语义处理等更高层次的操作奠定基础。

安装:

       

go get modernc.org/golex

文档:

lex package - modernc.org/golex/lex - Go Packages

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值