Files
Alessandro ScandoneandGitHub 5dfc34cd3c feat: feature flag syntax (#120)
* feat: feature flag syntax

* feat: change syntax

* feat: reject invalid features

* feat: add static check of unknown features

* feat: more defensive args handling

* fix: fix typo
2026-03-04 13:32:35 +00:00

151 lines
2.5 KiB
Go

package parser
import (
"fmt"
"strconv"
"strings"
)
type Version interface {
version()
String() string
}
type VersionMachine struct{}
func (v VersionMachine) version() {}
func (v VersionMachine) String() string {
return "machine"
}
type VersionInterpreter struct {
Major uint16
Minor uint16
Patch uint16
}
func NewVersionInterpreter(major uint16, minor uint16, patch uint16) VersionInterpreter {
return VersionInterpreter{
Major: major,
Minor: minor,
Patch: patch,
}
}
func (v VersionInterpreter) version() {}
func (v VersionInterpreter) String() string {
return fmt.Sprintf("interpreter %d.%d.%d", v.Major, v.Minor, v.Patch)
}
func (v VersionInterpreter) GtEq(other VersionInterpreter) bool {
if v.Major > other.Major {
return true
}
if v.Major < other.Major {
return false
}
if v.Minor < other.Minor {
return false
}
return v.Patch >= other.Patch
}
func parseSemanticVersion(src string) (bool, int, int, int) {
parts := strings.Split(src, ".")
if len(parts) < 3 {
return false, 0, 0, 0
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return false, 0, 0, 0
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return false, 0, 0, 0
}
patch, err := strconv.Atoi(parts[2])
if err != nil {
return false, 0, 0, 0
}
return true, major, minor, patch
}
func parseVersion(comment string) Version {
comment = strings.TrimLeft(comment, " ")
comment = strings.TrimRight(comment, " \n")
parts := strings.Split(comment, " ")
if len(parts) < 2 {
return nil
}
if parts[0] != "@version" {
return nil
}
switch parts[1] {
case "machine":
return VersionMachine{}
case "interpreter":
if len(parts) < 3 {
return nil
}
ok, major, minor, patch := parseSemanticVersion(parts[2])
if !ok {
return nil
}
return VersionInterpreter{
Major: uint16(major),
Minor: uint16(minor),
Patch: uint16(patch),
}
default:
return nil
}
}
func (p Program) GetVersion() Version {
for _, comment := range p.Comments {
v := parseVersion(comment.Content)
if v != nil {
return v
}
}
return nil
}
func parseFlag(comment string) string {
comment = strings.TrimLeft(comment, " ")
comment = strings.TrimRight(comment, " \n")
parts := strings.Split(comment, " ")
if len(parts) < 2 {
return ""
}
if parts[0] != "@feature_flag" {
return ""
}
return parts[1]
}
func (p Program) GetFlags() map[string]struct{} {
flags := make(map[string]struct{})
for _, comment := range p.Comments {
flag := parseFlag(comment.Content)
if flag != "" {
flags[flag] = struct{}{}
}
}
return flags
}