Golang代码审查:基于正则表达式的"tokenizer"库实现分析
Golang代码审查:基于正则表达式的"tokenizer"库实现分析 大家好,
这是我用GO编写的第一个库。这是一个基于正则表达式的"分词器"库:
gomillas/parser
一个基于正则表达式解析字符串的GO库 - gomillas/parser
这是一个概念验证。以下代码用于计算数学表达式:
package parser_test
import (
"errors"
"fmt"
"math"
"strconv"
"github.com/gomillas/parser"
)
func Example() {
const src = "125 + 2 * (sqrt 9 - 1) - 3"
m := parser.New(src)
if result, err := mathExp(m, "="); err == nil {
fmt.Println(result)
} else {
panic(
fmt.Errorf("%s (line: %d, col: %d)", err.Error(), m.Line(), m.Column()),
我希望能够得到一些评论,以及代码是否可以在某些方面进行改进。 谢谢。
更多关于Golang代码审查:基于正则表达式的"tokenizer"库实现分析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang代码审查:基于正则表达式的"tokenizer"库实现分析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
以下是针对您基于正则表达式的分词器库的代码审查分析。我将重点讨论设计模式、正则表达式使用、错误处理和性能优化等方面,并提供具体示例说明。
1. 正则表达式设计与效率
您的分词器核心依赖于正则表达式匹配,建议优化模式定义以提高性能:
// 当前实现可能存在的问题:重复编译正则表达式
// 建议预编译正则表达式模式
var (
numberRegex = regexp.MustCompile(`^\d+(\.\d+)?`)
operatorRegex = regexp.MustCompile(`^[+\-*/]`)
parenRegex = regexp.MustCompile(`^[()]`)
funcRegex = regexp.MustCompile(`^sqrt`)
)
func (m *Matcher) matchToken() (Token, error) {
// 按优先级匹配,避免回溯
if match := numberRegex.FindStringSubmatch(m.input[m.pos:]); match != nil {
return Token{Type: Number, Value: match[0]}, nil
}
if match := funcRegex.FindStringSubmatch(m.input[m.pos:]); match != nil {
return Token{Type: Function, Value: match[0]}, nil
}
// ... 其他匹配
}
2. 词法分析器状态管理
当前实现可能缺少明确的有限状态机,建议采用更结构化的状态转换:
type Lexer struct {
input string
pos int
tokens []Token
state lexState
}
type lexState int
const (
stateDefault lexState = iota
stateNumber
stateIdentifier
stateOperator
)
func (l *Lexer) tokenize() error {
for l.pos < len(l.input) {
switch l.state {
case stateDefault:
if unicode.IsDigit(rune(l.input[l.pos])) {
l.state = stateNumber
continue
}
// 其他状态转换
case stateNumber:
// 处理数字状态
if !unicode.IsDigit(rune(l.input[l.pos])) {
l.emitToken(TokenNumber)
l.state = stateDefault
continue
}
}
l.pos++
}
return nil
}
3. 错误处理与位置跟踪
改进错误报告机制,提供更精确的定位信息:
type ParseError struct {
Message string
Line int
Column int
Offset int
}
func (m *Matcher) expectToken(expectedType TokenType) (Token, error) {
token, err := m.nextToken()
if err != nil {
return Token{}, err
}
if token.Type != expectedType {
return Token{}, &ParseError{
Message: fmt.Sprintf("expected %s, got %s", expectedType, token.Type),
Line: m.line,
Column: m.column,
Offset: m.pos,
}
}
return token, nil
}
4. 内存分配优化
减少在分词过程中不必要的字符串分配:
func (l *Lexer) readNumber() string {
start := l.pos
for l.pos < len(l.input) && unicode.IsDigit(rune(l.input[l.pos])) {
l.pos++
}
// 直接返回切片,避免字符串复制
return l.input[start:l.pos]
}
// 使用字节切片而非字符串进行匹配
func matchPattern(input []byte, pattern *regexp.Regexp) bool {
return pattern.Match(input)
}
5. 递归下降解析器实现
针对数学表达式的解析,建议采用明确的递归下降方法:
func (p *Parser) parseExpression() (float64, error) {
left, err := p.parseTerm()
if err != nil {
return 0, err
}
for p.current().Type == Add || p.current().Type == Subtract {
op := p.current()
p.advance()
right, err := p.parseTerm()
if err != nil {
return 0, err
}
if op.Type == Add {
left += right
} else {
left -= right
}
}
return left, nil
}
func (p *Parser) parseTerm() (float64, error) {
left, err := p.parseFactor()
if err != nil {
return 0, err
}
for p.current().Type == Multiply || p.current().Type == Divide {
op := p.current()
p.advance()
right, err := p.parseFactor()
if err != nil {
return 0, err
}
if op.Type == Multiply {
left *= right
} else {
if right == 0 {
return 0, fmt.Errorf("division by zero")
}
left /= right
}
}
return left, nil
}
6. 测试覆盖与基准测试
添加全面的测试用例和性能基准:
func TestMathExpressions(t *testing.T) {
tests := []struct {
input string
expected float64
hasError bool
}{
{"2 + 3", 5, false},
{"(1 + 2) * 3", 9, false},
{"sqrt 16", 4, false},
{"1 / 0", 0, true},
}
for _, test := range tests {
m := parser.New(test.input)
result, err := mathExp(m, "=")
if test.hasError && err == nil {
t.Errorf("expected error for input %s", test.input)
}
if !test.hasError && result != test.expected {
t.Errorf("for input %s, expected %f, got %f", test.input, test.expected, result)
}
}
}
func BenchmarkTokenizer(b *testing.B) {
input := "125 + 2 * (sqrt 9 - 1) - 3"
for i := 0; i < b.N; i++ {
m := parser.New(input)
_, _ = mathExp(m, "=")
}
}
这些改进建议主要关注代码结构、性能优化和错误处理,将使您的分词器库更加健壮和高效。正则表达式分词器的关键在于平衡灵活性与性能,通过预编译模式和状态机设计可以显著提升处理速度。

