Идиоматический Go Lexer

Я написал код 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)
    }
}

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *