Я написал код Go, который дает желаемый результат, но я не уверен, что сам код хорошо «пахнет». Я хотел бы знать, содержит ли он какие-либо антипаттерны, иллюстрирует ли он плохие практики или не имеет надлежащих идиом, которые можно было бы ожидать от программы, написанной на Golang. Подробности программы приведены ниже.
Я недавно начал программировать на Go и искал способы попрактиковаться в своих навыках. Я родом из Python и в последнее время читал о дизайне компиляторов. Итак, пытаясь реализовать оба этих зарождающихся интереса, сегодня утром я написал немного кода Go, чтобы преобразовать строку в серию токенов. Лексический анализатор распознает числа, общие символы пробелов и бинарные операторы сложения и вычитания.
Я основал этот лексер на пример создания токенизатора Я нашел в документации Python для библиотеки re (regex). Таким образом, мой код использует пакет регулярного выражения стандартной библиотеки Go. Я планирую сегментировать код на отдельные файлы / пакеты, но пока у меня все в одном файле для удобства чтения.
Сам код ниже. Конкретные вопросы содержатся в комментариях, но, пожалуйста, поделитесь любыми проблемами, которые вы обнаружите с кодом. Мне нравится императивный и «простой» стиль Go, и я хотел бы убедиться, что в будущем у меня не возникнут какие-либо плохие привычки при написании кода Go.
Вот ссылка Go Playground на код: https://play.golang.org/p/jfuWRyOipMM
package main
import (
"regexp"
"strconv"
"strings"
"fmt"
)
// Is it best practice to enumerate string constants like this?
const (
Plus = "Plus"
Minus = "Minus"
Number = "Number"
Skip = "Skip"
Newline = "Newline"
)
// Token struct for keeping track of tokens
type Token struct {
Type string
// How to best signify that a value could be a string or an int?
Value interface{}
Line int
Column int
}
// Tokenize converts a string into a slice of tokens
func Tokenize(text string) []Token {
// Is this mapping a good way of keeping track of token patterns?
groupNamesPatterns := map[string]string{
Plus: `+`,
Minus: `-`,
Number: `[d]+`,
Skip: `[ t]`,
Newline: `n`,
}
// Create the Regex pattern with all the named groups
var patternStrings []string
for groupName, pattern := range groupNamesPatterns {
groupPattern := fmt.Sprintf(`(?P<%s>%s)`, groupName, pattern)
patternStrings = append(patternStrings, groupPattern)
}
pattern := regexp.MustCompile(strings.Join(patternStrings, "|"))
groupNames := pattern.SubexpNames()
// Create the tokens list
var tokens []Token
line := 1
column := 0
matches := pattern.FindAllStringSubmatch(text, -1)
for _, m := range matches {
// Iterate through match group names to find the name of the
// matched group
for i, matchedText := range m[1:] {
if matchedText != "" {
groupName := groupNames[i+1]
if groupName == Newline {
line += 1
column = 0
break
}
if groupName != Skip {
t := Token{groupName, "", line, column}
// Set the value of the token to either the parsed string
// or raw string
if parsed, err := strconv.Atoi(matchedText); err != nil {
t.Value = matchedText
} else {
t.Value = parsed
}
tokens = append(tokens, t)
}
column += len(matchedText)
break
}
}
}
return tokens
}
func main() {
// Example of tokenizing string spanning multiple lines
text := `
1
+ 2
- 3`
tokens := Tokenize(text)
for _, token := range tokens {
fmt.Printf("%#vn", token)
}
}
