Skip to content

Commit

Permalink
The langtools package supports JS versions (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
jalan authored May 27, 2021
1 parent 12fecb8 commit 38fd203
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 51 deletions.
5 changes: 5 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## v0.0.8 2021-05-27

* Fix several bugs in SemVer parsing.


## v0.0.7 2021-05-11

* Add support for parsing PHP versions.
Expand Down
18 changes: 0 additions & 18 deletions pkg/version/ruby.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,3 @@ func dropTrailingZeroes(segments []string) []string {
}
return segments[0 : lastNonzeroIndex+1]
}

func asciiToDecimalString(s string) string {
decimal := ""
for i, r := range s {
if i == 0 {
decimal = fmt.Sprintf("%d", r)
continue
}

if i == 1 {
decimal += "."
}

// Pad to 3 digits because ASCII characters are at most 3 digits
decimal += fmt.Sprintf("%03d", r)
}
return decimal
}
62 changes: 53 additions & 9 deletions pkg/version/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ package version
import (
"fmt"
"regexp"
"strconv"
"strings"

"golang.org/x/text/unicode/norm"
)

const (
// Value greater than unicode's upper limit of 0x10FFFF = 1,114,111
maxValue = "2000000"
delimiter = "-"
delimitedSubsection = delimiter + "$1" + delimiter
)
Expand All @@ -28,7 +27,7 @@ var (
notZero = regexp.MustCompile(`[^0]`)

// Matches semver 2.0
semVerRegEx = regexp.MustCompile(`^(?P<Major>\d+)\.(?P<Minor>\d+)\.(?P<Patch>\d+)(?P<PreReleaseIDs>-[0-9A-Za-z-.]+)?(?P<BuildMetadata>\+[0-9A-Za-z-.]+)?$`)
semVerRegEx = regexp.MustCompile(`^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`)

genericPreReleaseIdentifiers = map[string]string{
"alpha": "-26",
Expand Down Expand Up @@ -89,19 +88,46 @@ func ParseSemVer(version string) (*Version, error) {
return nil, fmt.Errorf("Version does not match semver regex: %s", version)
}

major, minor, patch, preReleaseIDs := matches[1], matches[2], matches[3], matches[4]
major, minor, patch, preRelease := matches[1], matches[2], matches[3], matches[4]
segments := []string{major, minor, patch}

if preReleaseIDs == "" {
segments = append(segments, maxValue)
} else {
ids := parseBySeparator(preReleaseIDs, anyPunctuationOrSeparator, toDecimalString)
segments = append(segments, ids...)
if preRelease != "" {
// This is here to make a pre-release always less than a normal
// release. For example "1.2.4-1" < "1.2.4"
segments = append(segments, "-1")

preReleaseSegments := parseSemVerPreRelease(preRelease)
segments = append(segments, preReleaseSegments...)

// And this is here to satisfy the requirement that "A larger
// set of pre-release fields has a higher precedence than a
// smaller set". For example, "1.0.0-alpha" < "1.0.0-alpha.0"
segments = append(segments, "-1")
}

return fromStringSlice(SemVer, version, segments)
}

func parseSemVerPreRelease(preRelease string) []string {
results := []string{}
segments := strings.Split(preRelease, ".")
for _, segment := range segments {
_, err := strconv.Atoi(segment)
if err != nil {
results = append(results, asciiToDecimalString(segment))
} else {
// This ensures that, for pre-releases, "Numeric
// identifiers always have lower precedence than
// non-numeric identifiers." For example,
// "1.2.3-5" < "1.2.3-4-foo"
results = append(results, "0")

results = append(results, segment)
}
}
return results
}

func normalizeUnicode(s string) string {
return norm.NFC.String(s)
}
Expand Down Expand Up @@ -223,3 +249,21 @@ func containsGenericPreReleaseIdentifierValue(numbers []string) bool {

return false
}

func asciiToDecimalString(s string) string {
decimal := ""
for i, r := range s {
if i == 0 {
decimal = fmt.Sprintf("%d", r)
continue
}

if i == 1 {
decimal += "."
}

// Pad to 3 digits because ASCII characters are at most 3 digits
decimal += fmt.Sprintf("%03d", r)
}
return decimal
}
103 changes: 79 additions & 24 deletions pkg/version/shared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,25 +112,33 @@ func TestParseSemVer(t *testing.T) {
version: "1.0",
expected: []string{},
},
"Number cannot have leading zero": {
version: "01.2.3",
expected: []string{},
},
"Another invalid input": {
version: "0.0.0-.",
expected: []string{},
},
"Parses Major.Minor.Patch": {
version: "1.2.3",
expected: []string{"1", "2", "3", maxValue},
expected: []string{"1", "2", "3"},
},
"Parses PreReleaseIdentifer": {
version: "1.2.3-a.1",
expected: []string{"1", "2", "3", "97", "1"},
expected: []string{"1", "2", "3", "-1", "97", "0", "1", "-1"},
},
"Parses alpha as pre-release": {
version: "1.2.3-alpha",
expected: []string{"1", "2", "3", "97.0000000108000000011200000001040000000097"},
expected: []string{"1", "2", "3", "-1", "97.108112104097", "-1"},
},
"Build Metadata Is Ignored": {
version: "1.2.3+ignored",
expected: []string{"1", "2", "3", maxValue},
expected: []string{"1", "2", "3"},
},
"Parses When All Sections Present": {
version: "1.2.3-a.1+ignored",
expected: []string{"1", "2", "3", "97", "1"},
expected: []string{"1", "2", "3", "-1", "97", "0", "1", "-1"},
},
}

Expand All @@ -149,25 +157,72 @@ func TestParseSemVer(t *testing.T) {
}
}

func TestParseSemVarOrdering(t *testing.T) {
alpha := parseOrFatalSemVer(t, "1.0.0-alpha")
alpha1 := parseOrFatalSemVer(t, "1.0.0-alpha.1")
alphaBeta := parseOrFatalSemVer(t, "1.0.0-alpha.beta")
beta := parseOrFatalSemVer(t, "1.0.0-beta")
beta2 := parseOrFatalSemVer(t, "1.0.0-beta.2")
beta11 := parseOrFatalSemVer(t, "1.0.0-beta.11")
rc := parseOrFatalSemVer(t, "1.0.0-rc.1")
stable := parseOrFatalSemVer(t, "1.0.0")
stableNext := parseOrFatalSemVer(t, "1.0.1")

assert.True(t, Compare(alpha, alpha1) < 0)
assert.True(t, Compare(alpha1, alphaBeta) < 0)
assert.True(t, Compare(alphaBeta, beta) < 0)
assert.True(t, Compare(beta, beta2) < 0)
assert.True(t, Compare(beta2, beta11) < 0)
assert.True(t, Compare(beta11, rc) < 0)
assert.True(t, Compare(rc, stable) < 0)
assert.True(t, Compare(stable, stableNext) < 0)
var testParseSemVerOrderInputs = []string{
"0.0.0-foo",
"0.0.0",
"0.0.1",
"0.1.2",
"0.9.0",
"0.9.9",
"0.10.0",
"0.99.0",
"1.0.0-alpha",
"1.0.0-alpha.0",
"1.0.0-alpha.1",
"1.0.0-alpha.100",
"1.0.0-alpha.100.0",
"1.0.0-alpha.100.a",
"1.0.0-alpha.beta",
"1.0.0-beta",
"1.0.0-beta.2",
"1.0.0-beta.11",
"1.0.0-rc.1",
"1.0.0",
"1.0.1",
"1.2.2",
"1.2.3-4",
"1.2.3-5",
"1.2.3-4-foo",
"1.2.3-5-Foo",
"1.2.3-5-foo",
"1.2.3-R2",
"1.2.3-a",
"1.2.3-a.0",
"1.2.3-a.5",
"1.2.3-a.10",
"1.2.3-a.100",
"1.2.3-a.b",
"1.2.3-a.b.c.5.d.100",
"1.2.3-a.b.c.10.d.5",
"1.2.3-alpha.0.2",
"1.2.3-alpha.0.pr.1",
"1.2.3-alpha.0.pr.2",
"1.2.3-asdf",
"1.2.3-pre",
"1.2.3-r100",
"1.2.3-r2",
"1.2.3",
"1.2.4-1",
"1.2.4",
"2.0.0",
"2.3.4",
"2.7.2+asdf",
"3.0.0",
"9.9.9-alpha.0.pr.1",
}

func TestParseSemVerOrdering(t *testing.T) {
for i := 0; i < len(testParseSemVerOrderInputs)-1; i++ {
v1 := parseOrFatalSemVer(t, testParseSemVerOrderInputs[i])
v2 := parseOrFatalSemVer(t, testParseSemVerOrderInputs[i+1])
assert.True(
t,
Compare(v1, v2) < 0,
"%v should be less than %v",
testParseSemVerOrderInputs[i],
testParseSemVerOrderInputs[i+1],
)
}
}

func TestIsNumber(t *testing.T) {
Expand Down

0 comments on commit 38fd203

Please sign in to comment.