@@ -21,6 +21,8 @@ open System Lean
2121
2222namespace Lake
2323
24+ /-! ## Parser Utils -/
25+
2426/--
2527Parses version components separated by a `.` from the head of the string.
2628
@@ -68,9 +70,53 @@ private def isWildVer (s : String.Slice) : Bool :=
6870
6971@[inline] private def parseVerNat (what : String) (s : String.Slice) : EStateM String σ Nat := do
7072 let some v := s.toNat?
71- | throw s! "expected numeral { what} version, got '{ s.copy} '"
73+ | throw s! "invalid { what} version: expected numeral , got '{ s.copy} '"
7274 return v
7375
76+ inductive VerComponent
77+ | none | wild | nat (n : Nat)
78+
79+ private def parseVerComponent {σ} (what : String) (s? : Option String.Slice) : EStateM String σ VerComponent := do
80+ let some s := s?
81+ | return .none
82+ if isWildVer s then
83+ return .wild
84+ let some n := s.toNat?
85+ | throw s! "invalid { what} version: expected numeral or wildcard, got '{ s.copy} '"
86+ return .nat n
87+
88+ def parseSpecialDescr (s : String) : EStateM String s.ValidPos String := do
89+ let p ← get
90+ if h : p = s.endValidPos then
91+ return ""
92+ else
93+ let c := p.get h
94+ if c == '-' then
95+ let p := p.next h
96+ let p' := nextUntilWhitespace p
97+ set p'
98+ let specialDescr := p.extract p'
99+ if specialDescr.isEmpty then
100+ throw "invalid version: '-' suffix cannot be empty"
101+ return specialDescr
102+ else
103+ return ""
104+ where
105+ nextUntilWhitespace p :=
106+ if h : p = s.endValidPos then
107+ p
108+ else if (p.get h).isWhitespace then
109+ p
110+ else
111+ nextUntilWhitespace (p.next h)
112+ termination_by s.utf8ByteSize - p.offset.byteIdx
113+ decreasing_by
114+ rw [p.byteIdx_offset_next]
115+ have := (p.get h).utf8Size_pos
116+ have := p.byteIdx_lt_utf8ByteSize h
117+ omega
118+
119+
74120private def runVerParse
75121 (s : String) (x : (s : String) → EStateM String s.ValidPos α)
76122 (startPos := s.startValidPos) (endPos := s.endValidPos)
@@ -84,19 +130,23 @@ private def runVerParse
84130 throw s! "unexpected characters at end of version: { tail} "
85131 | .error e _ => throw e
86132
133+ /-! ## SemVerCore -/
134+
87135/-- The major-minor-patch triple of a [ SemVer ] (https://semver.org/). -/
88136public structure SemVerCore where
89137 major : Nat := 0
90138 minor : Nat := 0
91139 patch : Nat := 0
92140 deriving Inhabited, Repr, DecidableEq, Ord
93141
142+ namespace SemVerCore
143+
94144public instance : LT SemVerCore := ltOfOrd
95145public instance : LE SemVerCore := leOfOrd
96146public instance : Min SemVerCore := minOfLe
97147public instance : Max SemVerCore := maxOfLe
98148
99- def SemVerCore. parseM (s : String) : EStateM String s.ValidPos SemVerCore := do
149+ def parseM (s : String) : EStateM String s.ValidPos SemVerCore := do
100150 try
101151 let cs ← parseVerComponents s
102152 if h : cs.size = 3 then
@@ -109,16 +159,20 @@ def SemVerCore.parseM (s : String) : EStateM String s.ValidPos SemVerCore := do
109159 catch e =>
110160 throw s! "invalid version core: { e} "
111161
112- @[inline] public def SemVerCore. parse (s : String) : Except String SemVerCore := do
162+ @[inline] public def parse (s : String) : Except String SemVerCore := do
113163 runVerParse s parseM
114164
115- public protected def SemVerCore. toString (ver : SemVerCore) : String :=
165+ public protected def toString (ver : SemVerCore) : String :=
116166 s! "{ ver.major} .{ ver.minor} .{ ver.patch} "
117167
118168public instance : ToString SemVerCore := ⟨SemVerCore.toString⟩
119169public instance : ToJson SemVerCore := ⟨(·.toString)⟩
120170public instance : FromJson SemVerCore := ⟨(do SemVerCore.parse <| ← fromJson? ·)⟩
121171
172+ end SemVerCore
173+
174+ /-! ## StdVer -/
175+
122176/--
123177A Lean-style semantic version.
124178A major-minor-patch triple with an optional arbitrary `-` suffix.
@@ -130,14 +184,16 @@ public structure StdVer extends SemVerCore where
130184/-- A Lean version. -/
131185public abbrev LeanVer := StdVer
132186
187+ namespace StdVer
188+
133189public instance : Coe StdVer SemVerCore := ⟨StdVer.toSemVerCore⟩
134190
135- @[inline] public def StdVer. ofSemVerCore (ver : SemVerCore) : StdVer :=
191+ @[inline] public def ofSemVerCore (ver : SemVerCore) : StdVer :=
136192 {toSemVerCore := ver, specialDescr := "" }
137193
138194public instance : Coe SemVerCore StdVer := ⟨StdVer.ofSemVerCore⟩
139195
140- public protected def StdVer. compare (a b : StdVer) : Ordering :=
196+ public protected def compare (a b : StdVer) : Ordering :=
141197 match compare a.toSemVerCore b.toSemVerCore with
142198 | .eq =>
143199 match a.specialDescr, b.specialDescr with
@@ -154,42 +210,15 @@ public instance : LE StdVer := leOfOrd
154210public instance : Min StdVer := minOfLe
155211public instance : Max StdVer := maxOfLe
156212
157- public def StdVer. parseM (s : String) : EStateM String s.ValidPos StdVer := do
213+ public def parseM (s : String) : EStateM String s.ValidPos StdVer := do
158214 let core ← SemVerCore.parseM s
159- let p ← get
160- if h : p = s.endValidPos then
161- return core
162- else
163- let c := p.get h
164- if c == '-' then
165- let p := p.next h
166- let p' := nextUntilWhitespace p
167- set p'
168- let specialDescr := p.extract p'
169- if specialDescr.isEmpty then
170- throw "invalid version: '-' suffix cannot be empty"
171- return {toSemVerCore := core, specialDescr}
172- else
173- return core
174- where
175- nextUntilWhitespace p :=
176- if h : p = s.endValidPos then
177- p
178- else if (p.get h).isWhitespace then
179- p
180- else
181- nextUntilWhitespace (p.next h)
182- termination_by s.utf8ByteSize - p.offset.byteIdx
183- decreasing_by
184- rw [p.byteIdx_offset_next]
185- have := (p.get h).utf8Size_pos
186- have := p.byteIdx_lt_utf8ByteSize h
187- omega
215+ let specialDescr ← parseSpecialDescr s
216+ return {toSemVerCore := core, specialDescr}
188217
189- @[inline] public def StdVer. parse (s : String) : Except String StdVer := do
218+ @[inline] public def parse (s : String) : Except String StdVer := do
190219 runVerParse s parseM
191220
192- public protected def StdVer. toString (ver : StdVer) : String :=
221+ public protected def toString (ver : StdVer) : String :=
193222 if ver.specialDescr.isEmpty then
194223 ver.toSemVerCore.toString
195224 else
@@ -199,6 +228,10 @@ public instance : ToString StdVer := ⟨StdVer.toString⟩
199228public instance : ToJson StdVer := ⟨(·.toString)⟩
200229public instance : FromJson StdVer := ⟨(do StdVer.parse <| ← fromJson? ·)⟩
201230
231+ end StdVer
232+
233+ /-! ## ToolchainVer -/
234+
202235/-- The `elan` toolchain file name (i.e., `lean-toolchain`). -/
203236public def toolchainFileName : FilePath := "lean-toolchain"
204237
@@ -301,6 +334,8 @@ end ToolchainVer
301334public def normalizeToolchain (s : String) : String :=
302335 ToolchainVer.ofString s |>.toString
303336
337+ /-! ## DecodeVersion -/
338+
304339/-- Parses a version from a string. -/
305340public class DecodeVersion (α : Type u) where
306341 decodeVersion : String → Except String α
@@ -311,9 +346,11 @@ public instance : DecodeVersion SemVerCore := ⟨SemVerCore.parse⟩
311346@[default_instance] public instance : DecodeVersion StdVer := ⟨StdVer.parse⟩
312347public instance : DecodeVersion ToolchainVer := ⟨(pure <| ToolchainVer.ofString ·)⟩
313348
349+ /-! ## VerRange -/
350+
314351public inductive Comparator
315352| lt | le | gt | ge | eq | ne
316- deriving Repr
353+ deriving Repr, Inhabited
317354
318355namespace Comparator
319356
@@ -366,6 +403,12 @@ public structure VerComparator where
366403
367404namespace VerComparator
368405
406+ /-- A version comparator that matches any version (i.e., `≥0.0.0`). -/
407+ def any : VerComparator :=
408+ {op := .ge, ver := .ofSemVerCore {}}
409+
410+ instance : Inhabited VerComparator := ⟨.any⟩
411+
369412def parseM (s : String) : EStateM String s.ValidPos VerComparator := do
370413 let op ← Comparator.parseM s
371414 let ver ← StdVer.parseM s
@@ -375,13 +418,28 @@ def parseM (s : String) : EStateM String s.ValidPos VerComparator := do
375418 runVerParse s parseM
376419
377420public def test (self : VerComparator) (ver : StdVer) : Bool :=
378- match self.op with
379- | .lt => ver < self.ver
380- | .le => ver ≤ self.ver
381- | .gt => ver > self.ver
382- | .ge => ver ≥ self.ver
383- | .eq => ver = self.ver
384- | .ne => ver ≠ self.ver
421+ match self.ver.specialDescr, ver.specialDescr with
422+ | _, "" =>
423+ match self.op with
424+ | .lt => ver < self.ver
425+ | .le => ver ≤ self.ver
426+ | .gt => ver > self.ver
427+ | .ge => ver ≥ self.ver
428+ | .eq => ver = self.ver
429+ | .ne => ver ≠ self.ver
430+ | "" , _ =>
431+ false
432+ | selfDescr, verDescr =>
433+ if self.ver.toSemVerCore = ver.toSemVerCore then
434+ match self.op with
435+ | .lt => verDescr < selfDescr
436+ | .le => verDescr ≤ selfDescr
437+ | .gt => verDescr > selfDescr
438+ | .ge => verDescr ≥ selfDescr
439+ | .eq => verDescr = selfDescr
440+ | .ne => verDescr ≠ selfDescr
441+ else
442+ false
385443
386444public protected def toString (self : VerComparator) : String :=
387445 s! "{ self.op}{ self.ver} "
@@ -394,7 +452,7 @@ public structure VerRange where
394452 private innerMk ::
395453 toString : String
396454 clauses : Array (Array VerComparator)
397- deriving Repr
455+ deriving Repr, Inhabited
398456
399457namespace VerRange
400458
@@ -433,6 +491,10 @@ where
433491 | .ok ands p =>
434492 go false ors ands p
435493 | .error e p => .error e p
494+ else if c == '^' then
495+ .error "caret ranges are unsupported; \
496+ if a specific major version is desired, use a tilde or wildcard range; \
497+ otherwise, use '≥' instead" p
436498 else if c == '~' then
437499 match parseTilde s ands (p.next h) with
438500 | .ok ands p =>
@@ -461,57 +523,50 @@ where
461523 | .ok cmp p =>
462524 go false ors (ands.push cmp) p
463525 | .error e p => .error e p
464- @[inline] appendRange ands minVer maxVer :=
465- let minVer := StdVer.ofSemVerCore minVer
526+ @[inline] appendRange ands minVer maxVer (specialDescr := "" ) :=
527+ let minVer := StdVer.mk minVer specialDescr
466528 let maxVer := StdVer.ofSemVerCore maxVer
467529 ands.push {op := .ge, ver := minVer} |>.push {op := .lt, ver := maxVer}
468- parseVerNat? {σ} what (s? : Option String.Slice) : EStateM String σ (Option Nat) := do
469- let some s := s?
470- | return none
471- if isWildVer s then
472- return none
473- let some n := s.toNat?
474- | throw s! "expected numeral or wildcard { what} version, got '{ s.copy} '"
475- return some n
476- parseWild s ands : EStateM String s.ValidPos _ := do
530+ parseWild (s : String) ands : EStateM String s.ValidPos _ := do
477531 let cs ← parseVerComponents s
478- if cs.size = 0 ∨ cs.size > 3 then
479- throw s! "invalid wildcard version: incorrect number of components: got { cs.size} , expected 1-3"
532+ if (← get).get?.any (· == '-' ) then
533+ throw s! "invalid wildcard range: wildcard versions do not support suffixes"
534+ else if cs.size = 0 ∨ cs.size > 3 then
535+ throw s! "invalid wildcard range: incorrect number of components: got { cs.size} , expected 1-3"
480536 else
481- let major? ← parseVerNat? "major" cs[0 ]?
482- let minor? ← parseVerNat? "minor" cs[1 ]?
483- let patch? ← parseVerNat? "patch" cs[2 ]?
484- if let some major := major? then
485- if let some minor := minor? then
486- if let some patch := patch? then
487- -- TODO: decide on an approach for plain versions
488- return ands.push {op := .eq, ver := .ofSemVerCore {major, minor, patch}}
489- else
490- return appendRange ands {major, minor} {major, minor := minor + 1 }
491- else if patch?.isSome then
492- throw s! "invalid patch version: components after a wildcard must be wildcards"
493- else
494- return appendRange ands {major} {major := major + 1 }
495- else if minor?.isSome then
496- throw "invalid minor version: components after a wildcard must be wildcards"
497- else if patch?.isSome then
537+ let major? ← parseVerComponent "major" cs[0 ]?
538+ let minor? ← parseVerComponent "minor" cs[1 ]?
539+ let patch? ← parseVerComponent "patch" cs[2 ]?
540+ match major?, minor?, patch? with
541+ | .nat major, .nat minor, .wild =>
542+ return appendRange ands {major, minor} {major, minor := minor + 1 }
543+ | .wild, _, .nat _ | _, .wild, .nat _ =>
498544 throw "invalid patch version: components after a wildcard must be wildcards"
499- else
500- return ands.push {op := .ge, ver := .ofSemVerCore {}}
501- parseTilde s ands : EStateM String s.ValidPos _ := do
545+ | .wild, .nat _, _ =>
546+ throw "invalid minor version: components after a wildcard must be wildcards"
547+ | .nat major, .wild, _ =>
548+ return appendRange ands {major} {major := major + 1 }
549+ | .nat _, _, _ =>
550+ throw "invalid version range: bare versions are not supported; \
551+ if you want to fix a specific version, use '=' before the full version; \
552+ otherwise, use '≥' to support it and future versions"
553+ | _, _, _ =>
554+ return ands.push .any
555+ parseTilde (s : String) ands : EStateM String s.ValidPos _ := do
502556 let cs ← parseVerComponents s
557+ let specialDescr ← parseSpecialDescr s
503558 if h : cs.size = 1 then
504559 let major ← parseVerNat "major" cs[0 ]
505- return appendRange ands {major} {major := major + 1 }
560+ return appendRange ands {major} {major := major + 1 } specialDescr
506561 else if h : cs.size = 2 then
507562 let major ← parseVerNat "major" cs[0 ]
508563 let minor ← parseVerNat "minor" cs[1 ]
509- return appendRange ands {major, minor} {major, minor := minor + 1 }
564+ return appendRange ands {major, minor} {major, minor := minor + 1 } specialDescr
510565 else if h : cs.size = 3 then
511566 let major ← parseVerNat "major" cs[0 ]
512567 let minor ← parseVerNat "minor" cs[1 ]
513568 let patch ← parseVerNat "patch" cs[2 ]
514- return appendRange ands {major, minor, patch} {major, minor := minor + 1 }
569+ return appendRange ands {major, minor, patch} {major, minor, patch := patch + 1 } specialDescr
515570 else
516571 throw s! "invalid tilde range: incorrect number of components: got { cs.size} , expected 1-3"
517572
0 commit comments