Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Aaaand rewrite the parser entirely, yet again, based on the tools giv…

…en by fsutil.
  • Loading branch information...
commit 50e816a508b18fed142ff7e00e6e1f0a70a54cfe 1 parent f1b4430
@robsimmons robsimmons authored
View
4 sources.cm
@@ -1,6 +1,7 @@
Group is
$/basis.cm
util/sources.cm
+ src/fsutil.sml
(* Smackage data types *)
src/protocol.sml
@@ -8,10 +9,9 @@ Group is
src/spec.sml
(* Managing local data *)
- src/fsutil.sml
src/version-index.sml
src/configure.sml
- src/install.sml
+ (* src/install.sml *)
(* Obtaining and manipulating code and packages *)
src/get-git.sml
View
4 sources.mlb
@@ -1,6 +1,7 @@
$(SML_LIB)/basis/basis.mlb
util/sources.mlb
+ src/fsutil.sml
(* Smackage data types *)
src/protocol.sml
@@ -8,10 +9,9 @@
src/spec.sml
(* Managing local data *)
- src/fsutil.sml
src/version-index.sml
src/configure.sml
- src/install.sml
+ (* src/install.sml *)
(* Obtaining and manipulating code and packages *)
src/get-git.sml
View
15 src/fsutil.sml
@@ -38,19 +38,22 @@ struct
| trimStart (#"\t"::t) = trimStart t
| trimStart l = l
- fun trimEnd (#"#"::t) = []
- | trimEnd (#"\n"::t) = []
- | trimEnd (h::t) = h :: trimEnd t
- | trimEnd [] = []
+ fun trimEnd (#"#"::t) accum = rev accum
+ | trimEnd (#"\n"::t) accum = rev accum
+ | trimEnd (h::t) accum = trimEnd t (h :: accum)
+ | trimEnd [] accum = rev accum
in
- String.implode (trimEnd (trimStart (String.explode s)))
+ String.implode (trimEnd (trimStart (String.explode s)) [])
end
fun getLines' trimmer splitter file =
let
fun loop accum stanzas =
case TextIO.inputLine file of
- NONE => rev (rev accum :: stanzas) before TextIO.closeIn file
+ NONE =>
+ if null accum
+ then (rev stanzas before TextIO.closeIn file)
+ else (rev (rev accum :: stanzas) before TextIO.closeIn file)
| SOME s =>
if splitter s
then (if null accum
View
18 src/main.sml
@@ -27,7 +27,9 @@ struct
( if null deps then raise NoDeps else ()
; if length deps = 1 then print "Resolving 1 dependency\n"
else print ("Resolving " ^ ltoi deps ^ "dependencies\n")
- ; app (fn (pkg, spec) => install pkg (SOME spec) true) deps
+ (* XXX here's the place to shortcut-stop if we have an acceptable
+ * version installed (issue #4) *)
+ ; app (fn (pkg, spec, _) => install pkg (SOME spec) true) deps
; print ("Done resolving dependencies for `" ^ pkg ^ "`\n"))
end handle NoDeps => () end
@@ -77,7 +79,7 @@ struct
if SmackLib.download (!Configure.smackHome) (pkg,ver,proto)
then print ( "Package `" ^ name ^ "` already installed.\n")
else ( print ( "Package `" ^ name ^ "` downloaded.\n")
- ; resolveDependencies pkg ver
+ ; resolveDependencies pkg ver (*
; (if runHooks then
(SmackLib.build
(!Configure.smackHome)
@@ -87,7 +89,7 @@ struct
(!Configure.smackHome)
(!Configure.platform,!Configure.compilers)
(pkg,ver)) else ())
- )
+ *))
end
(** Uninstall a package with a given name and version.
@@ -132,9 +134,11 @@ struct
val res = VersionIndex.search name
val _ = if length res = 0 then print "No packages found.\n" else ()
val _ = List.app
- (fn (n,v,p) =>
- print (n ^ " " ^ SemVer.toString v ^ " (from " ^
- Protocol.toString p ^ ")\n")) res
+ (fn (n,dict) =>
+ SemVerDict.app
+ (fn (v, p) =>
+ print (n ^ " " ^ SemVer.toString v ^ " (from " ^
+ Protocol.toString p ^ ")\n")) dict) res
in
()
end
@@ -336,6 +340,8 @@ struct
(TextIO.output (TextIO.stdErr, s ^ "\n"); OS.Process.failure)
| (Fail s) =>
(TextIO.output (TextIO.stdErr, s ^ "\n"); OS.Process.failure)
+ | (Spec.SpecError s) =>
+ (TextIO.output (TextIO.stdErr, s ^ "\n"); OS.Process.failure)
| exn =>
(TextIO.output (TextIO.stdErr, "UNEXPECTED ERROR: "
^ exnMessage exn ^ "\n")
View
16 src/semver.sml
@@ -1,6 +1,7 @@
signature SEMVER =
sig
eqtype semver (* v0.2.4beta, v1.2.3, etc... *)
+ type t = semver
type constraint (* v1, v1.2, v2.3.6, v3.1.6, etc... *)
exception InvalidVersion
@@ -40,6 +41,7 @@ end
structure SemVer:> SEMVER =
struct
type semver = int * int * int * string option
+ type t = semver
type constraint = int * int option * int option * string option
exception InvalidVersion
@@ -53,12 +55,18 @@ struct
fun fromString' s =
let
- val s' = if String.sub (s,0) = #"v"
- then String.extract (s, 1, NONE)
- else s
+ fun fail () = raise Fail ("`" ^ s ^ "` not a valid semantic version")
+
+ val s' =
+ case String.tokens Char.isSpace s of
+ [ s ] =>
+ if String.sub (s,0) = #"v"
+ then String.extract (s, 1, NONE)
+ else s
+ | _ => fail ()
+
val f = String.fields (fn #"." => true | _ => false) s'
- fun fail () = raise Fail ("`" ^ s ^ "` not a valid semantic version")
fun vtoi i =
case Int.fromString i of
NONE => fail ()
View
2  src/smackage-path.sml
@@ -56,7 +56,7 @@ struct
then raise Metadata ("Spec file not found: " ^ specFile) else
if not (OS.FileSys.access (specFile, [ OS.FileSys.A_READ ]))
then raise Metadata "Spec file exists but can't be read"
- else Spec.fromFile specFile
+ else Spec.fromFile specFile
handle (Spec.SpecError s) => raise Fail ("Spec error: " ^ s)
end
View
21 src/smacklib.sml
@@ -4,6 +4,7 @@ sig
** was already there *)
val download : string -> string * SemVer.semver * Protocol.protocol -> bool
+(*
(** Build a previously downloaded package by invoking a command
specified as 'build:' in the spec file. *)
val build : string -> (string * string list) ->
@@ -13,6 +14,7 @@ sig
specified as 'install:' in the spec file. *)
val install : string -> (string * string list) ->
(string * SemVer.semver) -> unit;
+*)
(** Returns a list of installed versions *)
(* XXX should probably be sorted, relies on the filesystem for this now *)
@@ -45,6 +47,7 @@ struct
; SmackagePath.createVersionLinks smackage_root (pkg,ver)
; false)
+(*
fun build smackage_root host (pkg,ver) =
let
val pkgDir = (smackage_root // "lib" // pkg // "v"^SemVer.toString ver)
@@ -68,6 +71,7 @@ struct
end handle (Spec.SpecError _) => () (* Silently fail if there is no spec. *)
fun uninstall smackage_root (pkg,ver) = raise Fail "Not implemented"
+*)
fun versions smackage_root pkg =
let
@@ -87,11 +91,14 @@ struct
end
fun info smackage_root (pkg,ver) =
- Spec.fromFile
- ( smackage_root
- // "lib"
- // pkg
- // ("v" ^ SemVer.toString ver)
- // (pkg ^ ".smackspec"))
- handle (Spec.SpecError s) => raise Fail ("Spec error: " ^ s)
+ let
+ val file =
+ ( smackage_root
+ // "lib"
+ // pkg
+ // ("v" ^ SemVer.toString ver)
+ // (pkg ^ ".smackspec"))
+ in
+ Spec.fromFile file
+ end handle (Spec.SpecError s) => raise Fail ("Spec error: " ^ s)
end
View
406 src/spec.sml
@@ -1,6 +1,6 @@
(*
- This defines the data structure and syntax for smackage package definitions (smackspec files).
- The general syntax is a flat key/value format, eg.
+ This defines the data structure and syntax for smackage package definitions
+ (smackspec files). The general syntax is a flat key/value format, eg:
provides: test 1.2.3beta
description: This is a sample smackspec file.
@@ -8,21 +8,12 @@
requires: smacklib >= 1.2.3
requires: ioextras 0.0.45
- Lines that aren't indented or empty must contain a key name followed by a colon (with no whitspace before the colon).
- The string to the right of the colon as well as any following indented or empty lines constitutes the value belonging
- to that key. There is no order and keys may occur multiple times. Lines with only whitespace at the beginning of the
- file are ignored. Keys that have no meaning in smackspec are syntax errors.
-
- The values have further syntactic restrictions depending on the key, vaguely summerized here:
-
- provides: PACKAGE_NAME SEMANTIC_VERSION (exactly once)
- description: ANY_STRING (at most once)
- remote: TYPE URL (exactly once)
- requires: PACKAGE_NAME VERSION_CONSTRAINTS (zero or more)
- comment: ANY_STRING (zero or more)
-
- Apart from that, the following keys are supported, but their values are not checked for syntax errors at the moment:
-
+ The following keys are supported:
+
+ description: ANY_STRING
+ remote: TYPE URL
+ requires: PACKAGE_NAME PARTIAL_SEMVER [optional: (MINIMAL_SEMVER)]
+ comment: ANY_STRING
maintainer: FULL_NAME <EMAIL_ADDRESS>
keywords: KEYWORD_1 KEYWORD_2 KEYWORD_3
upstream-version: VERSION
@@ -36,217 +27,194 @@
install: COMMAND
uninstall: COMMAND
documentation: COMMAND
-
- All of these keys can appear at most once.
- Please note that the parser is rather lax at the moment; it will accept url values that aren't really URLs etc.
- This will likely change in the future, so please be careful to get it right when pasting or typing in the values.
+ See https://github.com/standardml/smackage/wiki/Smackspec for more
+ information. Please note that the parser is rather lax at the moment; it
+ will accept url values that aren't really URLs, etc. This will likely
+ change in the future.
*)
-
+structure SemVerDict = ListDict (structure Key = SemVer)
+structure StringDict =
+ ListDict
+ (structure Key = struct type t = string val compare = String.compare end)
signature SPEC =
sig
- exception SpecError of string
-
- datatype spec_entry =
- Provides of string * SemVer.semver
- | Description of string
- | Requires of string * SemVer.constraint
- | Maintainer of string
- | Remote of Protocol.protocol
- | License of string
- | Platform of string
- | Key of string * string
-
- type spec
-
- (* Parses a smackspec file. *)
- val fromFile : string -> spec
-
- (* Parses a smackspec string. *)
- val fromString : string -> spec
- val toString : spec -> string
- val toVersionSpec : spec -> string * SemVer.semver * Protocol.protocol
-
- (* Helpers for extracting things from specs *)
- val provides : spec -> string * SemVer.semver
- val requires : spec -> (string * SemVer.constraint) list
- val remote : spec -> Protocol.protocol
- val platforms : spec -> (string * spec) list
- val key : spec -> string -> string
+ exception SpecError of string
+
+ datatype spec_entry =
+ Provides of string * SemVer.semver
+ | Description of string
+ | Requires of string * SemVer.constraint * SemVer.semver option
+ | Maintainer of string
+ | Remote of Protocol.protocol
+ | License of string
+ | Platform of string
+ | Key of string * string
+
+ type spec = spec_entry list
+
+ (* Parse a smackspec file (every line should be an empty string or a valid
+ * spec_entry, such as one would get from FSUtil.getLines) *)
+ val parse: string list -> spec
+ val fromFile: string -> spec
+ val toString: spec -> string
+
+ (* Interprets the spec as a packages file, get the requirements *)
+ val key: spec -> string -> string list
+ val platforms: spec -> (string * spec) list
+ val provides: spec -> string * SemVer.semver
+ val remote: spec -> Protocol.protocol
+ val requires:
+ spec -> (string * SemVer.constraint * SemVer.semver option) list
+
+ (* Interprests a series of specs as a versions.smackspec file *)
+ val toVersionIndex:
+ spec list -> Protocol.protocol SemVerDict.dict StringDict.dict
end
-structure Spec : SPEC =
+structure Spec:> SPEC =
struct
- exception SpecError of string
-
- datatype spec_entry =
- Provides of string * SemVer.semver
- | Description of string
- | Requires of string * SemVer.constraint
- | Maintainer of string
- | Remote of Protocol.protocol
- | License of string
- | Platform of string
- | Key of string * string (* We just push all the unused keys in here *)
-
- type spec = spec_entry list
-
- fun trim s =
- let
- fun trimStart (#" "::t) = trimStart t
- | trimStart (#"\t"::t) = trimStart t
- | trimStart l = l
-
- fun trimEnd (#"#"::t) = []
- | trimEnd (#"\n"::t) = []
- | trimEnd (h::t) = h :: trimEnd t
- | trimEnd [] = []
- in
- String.implode (trimEnd (trimStart (String.explode s)))
- end
-
- (* Like String.fields, but split at most once *)
- fun splitOnce delim s =
- case CharVector.findi (fn (_,c) => c = delim) s of
- NONE => [s]
- | SOME (i,_) =>
- [String.extract (s,0,SOME i), String.extract (s,i+1,NONE)]
-
- fun parsePackage s =
- (fn [pkg,ver] => (pkg, SemVer.fromString ver)
- | _ => raise SpecError ("Invalid 'provides:' content: `" ^ s ^ "'"))
- (String.fields Char.isSpace s)
-
- fun parseRequires s =
- (fn [pkg,con] => (pkg,SemVer.constrFromString con)
- | _ => raise SpecError ("Invalid 'requires:' content: `" ^ s ^ "'"))
- (String.fields Char.isSpace s)
-
- fun parseLine line =
- if String.isPrefix "#" line orelse line = "\n" orelse line = ""
- then NONE else
- let
- val f = splitOnce #":" line
- val _ = if length f <> 2 then
- raise SpecError ("Malformed line in spec: `" ^ line ^ "'")
- else ()
- val (key,value) = (List.nth(f,0), trim (List.nth(f,1)))
- val _ =
- if CharVector.all (fn c => Char.isAlphaNum c orelse c = #"-") key
- then ()
- else raise SpecError ("Invalid key in spec: `"^key^"'")
- in
- case (key,value) of
- ("provides",v) => SOME (Provides (parsePackage v))
- | ("description",v) => SOME (Description v)
- | ("requires",v) => SOME (Requires (parseRequires v))
- | ("maintainer",v) => SOME (Maintainer v)
- | ("remote",v) => SOME (Remote (Protocol.fromString v))
- | ("license",v) => SOME (License v)
- | ("platform",v) => SOME (Platform v)
- | (k,v) => SOME (Key (k,v))
- end
-
- fun parse lines =
- List.mapPartial (parseLine o trim) lines
-
- fun readLines (file, position, lines) =
- case TextIO.inputLine file of
- NONE => rev lines
- | (SOME "\n") => readLines (file, position + 1, lines)
- | (SOME line) =>
- readLines (file, position + 1, line :: lines)
-
-
- fun parseStream stream =
- let
- val lines = readLines (stream, 1, [])
- in
- parse lines
- end handle (e as SpecError s) => (
- TextIO.output (TextIO.stdErr, "Error in smackspec: " ^ s ^ "\n");
- raise e
- )
-
-
- fun fromFile filename =
- let
- val file = TextIO.openIn filename
- val result = parseStream file
- val _ = TextIO.closeIn file
- in
- result
- end
-
- fun fromString string = parseStream (TextIO.openString string)
-
- fun withErrorPrinter parser name input = parser input
- handle (e as SpecError s) => (
- TextIO.output (TextIO.stdErr, "Error in '" ^ name ^ "': " ^ s ^ "\n");
- raise e
- )
-
- fun toString' (Provides (s,v)) =
- "provides: " ^ s ^ " " ^ SemVer.toString v
- | toString' (Description s) =
- "description: " ^ s
- | toString' (Requires (p,v)) =
- "requires: " ^ p ^ " " ^ SemVer.constrToString v
- | toString' (Maintainer s) =
- "maintainer: " ^ s
- | toString' (Remote p) =
- "remote: " ^ Protocol.toString p
- | toString' (License s) =
- "license: " ^ s
- | toString' (Platform s) =
- "platform: " ^ s
- | toString' (Key (k,v)) = k ^ ": " ^ v
-
- fun toString spec =
- String.concatWith "\n" (map toString' spec)
-
- fun provides s =
- (fn (Provides v) => v | _ =>
- raise SpecError "Missing provides line in spec")
- (hd (List.filter (fn (Provides _) => true | _ => false) s))
- handle _ => raise SpecError "Missing provides: line in spec"
-
- val requires =
- List.mapPartial (fn (Requires v) => SOME v | _ => NONE)
-
- fun remote s =
- (fn (Remote v) => v | _ =>
- raise SpecError "Missing remote: line in spec")
- (hd (List.filter (fn (Remote _) => true | _ => false) s))
- handle _ => raise SpecError "Missing remote: line in spec"
-
-
- fun toVersionSpec (spec : spec) =
- (fn (pkg,ver) => (pkg,ver,remote spec)) (provides spec)
- handle (e as SpecError s) => (
- TextIO.output (TextIO.stdErr,
- "Error in smackspec: " ^ s ^ "\n" ^ toString spec);
- raise e)
-
- fun platform2spec (s,[]) = (s,[])
- | platform2spec (s, l as (Platform p :: t)) = (s,l)
- | platform2spec (s,h::t) = platform2spec (s @ [h], t)
-
- fun platforms (Platform p :: t) =
- let
- val (cont,t') = platform2spec ([],t)
- in
- (p,cont) :: platforms t'
- end
- | platforms (h::t) = platforms t
- | platforms [] = []
-
- fun key [] name = raise SpecError ("Key `" ^ name ^ "' not found in spec.")
- | key ((Key(k,v)::t)) name = if k = name then v else key t name
- | key (h::t) name = key t name
-
+ exception SpecError of string
+
+ datatype spec_entry =
+ Provides of string * SemVer.semver
+ | Description of string
+ | Requires of string * SemVer.constraint * SemVer.semver option
+ | Maintainer of string
+ | Remote of Protocol.protocol
+ | License of string
+ | Platform of string
+ | Key of string * string (* We just push all the unused keys in here *)
+
+ type spec = spec_entry list
+
+ (* Like String.fields, but split at most once *)
+ fun splitOnce delim s =
+ case CharVector.findi (fn (_, c) => c = delim) s of
+ NONE => (s, NONE)
+ | SOME (i, _) =>
+ (String.extract (s,0,SOME i), SOME (String.extract (s,i+1,NONE)))
+
+ fun parsePackage s =
+ case String.tokens Char.isSpace s of
+ [pkg, ver] => (pkg, SemVer.fromString ver)
+ | _ => raise SpecError ("Invalid 'provides:' content: `" ^ s ^ "'")
+
+ fun parseRequires s =
+ case String.tokens Char.isSpace s of
+ [pkg, con] => (pkg, SemVer.constrFromString con, NONE)
+ | [pkg, con, min] =>
+ if #"(" = String.sub (min, 0)
+ andalso #")" = String.sub (min, size min - 1)
+ then ( pkg
+ , SemVer.constrFromString con
+ , SOME (SemVer.fromString
+ (String.substring (min, 1, size min - 2))))
+ else raise SpecError ("Invalid minimal version: `" ^ min ^ "'")
+ | _ => raise SpecError ("Invalid 'requires:' content: `" ^ s ^ "'")
+
+ fun parseLine "" = NONE
+ | parseLine line =
+ let
+ val (key, value) =
+ case splitOnce #":" line of
+ (key, SOME value) => (key, value)
+ | _ => raise SpecError ("Malformed line in spec: `" ^ line ^ "'")
+ val () =
+ if CharVector.all (fn c => Char.isAlphaNum c orelse c = #"-") key
+ then ()
+ else raise SpecError ("Invalid key in spec: `"^key^"'")
+ in case (key,value) of
+ ("provides",v) => SOME (Provides (parsePackage v))
+ | ("description",v) => SOME (Description v)
+ | ("requires",v) => SOME (Requires (parseRequires v))
+ | ("maintainer",v) => SOME (Maintainer v)
+ | ("remote",v) => SOME (Remote (Protocol.fromString v))
+ | ("license",v) => SOME (License v)
+ | ("platform",v) => SOME (Platform v)
+ | (k,v) => SOME (Key (k,v))
+ end
+
+ fun parse lines = List.mapPartial parseLine lines
+
+ val fromFile = parse o FSUtil.getCleanLines o TextIO.openIn
+
+ fun toString' (Provides (s,v)) =
+ "provides: " ^ s ^ " " ^ SemVer.toString v
+ | toString' (Description s) =
+ "description: " ^ s
+ | toString' (Requires (p,v,min)) =
+ ( "requires: " ^ p ^ " " ^ SemVer.constrToString v
+ ^ (case min of NONE => "" | SOME v => "(" ^ SemVer.toString v ^ ")"))
+ | toString' (Maintainer s) =
+ "maintainer: " ^ s
+ | toString' (Remote p) =
+ "remote: " ^ Protocol.toString p
+ | toString' (License s) =
+ "license: " ^ s
+ | toString' (Platform s) =
+ "platform: " ^ s
+ | toString' (Key (k,v)) = k ^ ": " ^ v
+
+ fun toString spec = String.concatWith "\n" (map toString' spec)
+
+ (* Helper functions *)
+
+ fun key s key =
+ let fun key' (key', v) = if key = key' then SOME v else NONE
+ in
+ List.mapPartial (fn (Key kv) => key' kv | _ => NONE) s
+ end
+
+ fun provides s =
+ case List.mapPartial (fn (Provides v) => SOME v | _ => NONE) s of
+ [] => raise SpecError "Missing `provides:' line in spec"
+ | [ v ] => v
+ | _ => raise SpecError "Multiple `provides:' lines in spec"
+
+ fun platforms [] = []
+ | platforms (Platform p :: t) =
+ let
+ fun loop [] s accum plats = rev ((s, rev accum) :: plats)
+ | loop (Platform p :: t) s accum plats =
+ loop t p [] ((s, rev accum) :: plats)
+ | loop (h :: t) s accum plats =
+ loop t s (h :: accum) plats
+ in
+ loop t p [] []
+ end
+ | platforms (h::t) = platforms t
+
+ fun remote s =
+ case List.mapPartial (fn (Remote v) => SOME v | _ => NONE) s of
+ [] => raise SpecError "Missing `remote:' line in spec"
+ | [ v ] => v
+ | _ => raise SpecError "Multiple `remote:' lines in spec"
+
+ val requires =
+ List.mapPartial (fn (Requires v) => SOME v | _ => NONE)
+
+ fun toVersionIndex (spec: spec list) =
+ let
+ fun folder (spec, dict) =
+ let
+ val remote = remote spec
+ val provides =
+ List.mapPartial (fn (Provides v) => SOME v | _ => NONE) spec
+ in
+ List.foldr
+ (fn ((pkg, semver), dict) =>
+ StringDict.insertMerge dict pkg
+ (SemVerDict.singleton semver remote)
+ (fn dict => SemVerDict.insert dict semver remote))
+ dict
+ provides
+ end
+ in
+ List.foldr folder StringDict.empty spec
+ end
end
View
87 src/version-index.sml
@@ -21,80 +21,41 @@ sig
string -> SemVer.constraint option -> SemVer.constraint * SemVer.semver
(* Rough search for a package name *)
- val search : string -> (string * SemVer.semver * Protocol.protocol) list
+ val search : string -> (string * Protocol.protocol SemVerDict.dict) list
end =
struct
fun // (dir, file) = OS.Path.joinDirFile { dir = dir, file = file }
infix 5 //
- val versionIndex = ref [] : (string * SemVer.semver * Protocol.protocol) list ref
+ val versionIndex: Protocol.protocol SemVerDict.dict StringDict.dict ref =
+ ref StringDict.empty
- (** Parse the versions.smackspec file to produce a list of available
- (package,version,protocol) triples. *)
- fun parseVersionsSpec smackage_root =
- let
- val fp = TextIO.openIn (smackage_root // "versions.smackspec")
-
- val stanza = ref "";
-
- fun readStanzas () =
- let
- val line = TextIO.inputLine fp
- in
- if line = NONE then [!stanza] else
- if line = SOME "\n"
- then (!stanza before stanza := "") :: readStanzas ()
- else (stanza := (!stanza) ^ (valOf line); readStanzas ())
- end
-
- val stanzas = readStanzas () handle _ => (TextIO.closeIn fp; [])
-
- fun whitespace s =
- let
- fun ws [] = true
- | ws (#"\n"::t) = ws t
- | ws (#"\r"::t) = ws t
- | ws (#" "::t) = ws t
- | ws (#"\t"::t) = ws t
- | ws _ = false
- in
- ws (String.explode s)
- end
- val _ = TextIO.closeIn fp
+ fun init smackage_root =
+ let
+ val specstanzas =
+ FSUtil.getStanzas
+ (TextIO.openIn (smackage_root // "versions.smackspec"))
in
- map (Spec.toVersionSpec o Spec.fromString)
- (List.filter (fn s => not (whitespace s)) stanzas)
+ versionIndex := Spec.toVersionIndex (map Spec.parse specstanzas)
end
- fun init smackage_root =
- versionIndex := parseVersionsSpec smackage_root
-
- fun isKnown pkg =
- not (null (List.filter (fn (n,_,_) => pkg = n) (!versionIndex)))
-
- fun queryVersions pkg = List.filter (fn (n,_,_) => pkg = n) (!versionIndex)
+ fun isKnown pkg = StringDict.member (!versionIndex) pkg
- fun latestVersion pkg =
- let
- val cand = queryVersions pkg
- val _ = if length cand = 0 then
- raise Fail ("Package `"^pkg^"' not found") else ()
- in
- List.foldl (fn ((n,v,p),v') => if SemVer.>(v,v')
- then v else v') (#2 (hd cand)) cand
- end
+ fun queryVersions pkg =
+ case StringDict.find (!versionIndex) pkg of
+ NONE => []
+ | SOME dict => SemVerDict.domain dict
fun getProtocol pkg ver =
- (SOME (#3 (hd
- (List.filter (fn (n,v,p) => (n = pkg andalso SemVer.eq (v, ver)))
- (!versionIndex))))) (* handle _ => NONE *)
+ Option.mapPartial (fn dict => SemVerDict.find dict ver)
+ (StringDict.find (!versionIndex) pkg)
fun name pkg NONE = pkg
| name pkg (SOME spec) = pkg ^ " " ^ SemVer.constrToString spec
- fun getAll pkg NONE = map #2 (queryVersions pkg)
+ fun getAll pkg NONE = queryVersions pkg
| getAll pkg (SOME spec) =
- List.filter (SemVer.satisfies spec) (map #2 (queryVersions pkg))
+ List.filter (SemVer.satisfies spec) (queryVersions pkg)
fun getLatest pkg constraint =
let
@@ -102,13 +63,13 @@ struct
val cand' =
case constraint of
NONE => cand
- | SOME spec => List.filter (SemVer.satisfies spec o #2) cand
+ | SOME spec => List.filter (SemVer.satisfies spec) cand
val () = if length cand > 0 then ()
else raise Fail ("Could not satisfy constraint `"
^ name pkg constraint ^ "`")
val best =
- List.foldl (fn ((n,v,p),v') => if SemVer.>(v,v') then v else v')
- (#2 (hd cand)) cand
+ List.foldl (fn (v,v') => if SemVer.>(v,v') then v else v')
+ (hd cand) cand
in
( (case constraint of NONE => SemVer.major best | SOME spec => spec)
, best)
@@ -116,13 +77,15 @@ struct
fun getBest pkg constraint =
let in
- case SemVer.intelligentSelect constraint (map #2 (queryVersions pkg)) of
+ case SemVer.intelligentSelect constraint (queryVersions pkg) of
NONE => raise Fail ("Could not satisfy constraint `"
^ name pkg constraint ^ "`")
| SOME (ver, spec) => (ver, spec)
end
fun search query =
- List.filter (fn (n,_,_) => String.isSubstring query n) (!versionIndex)
+ List.filter
+ (fn (pkg, versions) => String.isSubstring query pkg)
+ (StringDict.toList (!versionIndex))
end
Please sign in to comment.
Something went wrong with that request. Please try again.