atomo-0.1.1: src/Atomo/Kernel/Eco.hs
{-# LANGUAGE QuasiQuotes #-}
module Atomo.Kernel.Eco where
import Atomo.Environment
import Atomo.Haskell
load :: VM ()
load = loadVersions >> loadEco
loadEco :: VM ()
loadEco = mapM_ eval [$es|
Eco =
Object clone do:
{ -- all packages OK for loading
-- name -> [package]
packages = []
-- versions loaded by @use:
-- name -> package
loaded = []
}
Eco Package =
Object clone do:
{ version = 0 . 1
dependencies = []
include = []
executables = []
}
(e: Eco) initialize :=
e initialize: (Eco Package load-from: "package.eco")
(e: Eco) initialize: pkg :=
{ pkg dependencies each: { c |
e packages (find: c from) match: {
@none -> raise: @(package-unavailable: c from needed: c to)
-- filter out versions not satisfying our dependency
@(ok: d) ->
{ safe = d to (filter: { p | p version join: c to })
safe match: {
[] ->
raise: @(no-versions-of: d satisfy: c to for: pkg name)
-- initialize the first safe version of the package
(p . _) ->
{ -- remove unsafe versions from the ecosystem
d to = safe
e initialize: p
} call
}
} call
}
}
@ok
} call
(e: Eco) load :=
{ eco = Directory home </> ".eco" </> "lib"
when: Directory (exists?: eco) do: {
e packages =
Directory (contents: eco) map:
{ c |
versions = Directory (contents: (eco </> c))
pkgs = versions map:
{ v |
Eco Package load-from:
(eco </> c </> v </> "package.eco")
}
c -> pkgs (sort-by: { a b | a version < b version })
}
}
@ok
} call
Eco path-to: (c: Eco Package) :=
Eco path-to: c name version: c version
Eco path-to: (name: String) :=
Directory home </> ".eco" </> "lib" </> name
Eco path-to: pkg version: (v: Version) :=
(Eco path-to: pkg) </> (v as: String)
Eco executable: (name: String) :=
Directory home </> ".eco" </> "bin" </> name
Eco which-main: (path: String) :=
if: File (exists?: (path </> "main.hs"))
then: { "main.hs" }
else: { "main.atomo" }
Eco install: (path: String) :=
{ pkg = Eco Package load-from: (path </> "package.eco")
target = Eco path-to: pkg
contents = (Eco which-main: path) . ("package.eco" . pkg include)
Directory create-tree-if-missing: target
contents each:
{ c |
if: Directory (exists?: (path </> c))
then: { Directory copy: (path </> c) to: (target </> c) }
else: { File copy: (path </> c) to: (target </> c) }
}
pkg executables each:
{ e |
exe =
[ "#!/usr/bin/env atomo"
"load: " .. (path </> e to) show
] unlines
with-output-to: (Eco executable: e from) do: { exe print }
File set-executable: (Eco executable: e from) to: True
}
@ok
} call
Eco uninstall: (name: String) version: (version: Version) :=
{ path = Eco path-to: name version: version
pkg = Eco Package load-from: (path </> "package.eco")
Directory remove-recursive: path
pkg executables each:
{ e |
File remove: (Eco executable: e from)
}
@ok
} call
Eco uninstall: (name: String) :=
{ Directory (contents: (Eco path-to: name)) each:
{ v |
Eco uninstall: name version: (v as: Version)
}
@ok
} call
Eco Package load-from: (file: String) :=
Eco Package clone do: { load: file }
(p: Eco Package) name: (n: String) :=
p name = n
(p: Eco Package) description: (d: String) :=
p description = d
(p: Eco Package) version: (v: Version) :=
p version = v
(p: Eco Package) author: (a: String) :=
p author = a
(p: Eco Package) include: (l: List) :=
p include = l
(p: Eco Package) depends-on: (ds: List) :=
p dependencies = ds map:
{ d |
if: (d is-a?: Association)
then: { d }
else: { d -> { True } }
}
(p: Eco Package) executables: (es: List) :=
p executables = es
context use: (name: String) :=
context use: name version: { True }
context use: (name: String) version: (v: Version) :=
context use: name version: { == v }
context use: (name: String) version: (check: Block) :=
Eco loaded (lookup: name) match: {
@none ->
(Eco packages (lookup: name) match: {
@none -> raise: @(package-unavailable: name)
@(ok: []) -> raise: @(no-package-versions: name)
@(ok: pkgs) ->
{ satisfactory = pkgs filter: { p | p version join: check }
when: satisfactory empty? do: {
raise: @(no-versions-of: (name -> versions) satisfy: check)
}
Eco loaded << (name -> satisfactory head)
path = Eco path-to: satisfactory head
context load: (path </> (Eco which-main: path))
@ok
} call
})
@(ok: p) ->
when: (p version join: check) not
do: { raise: @(incompatible-version-loaded: name needed: check) }
}
Eco load
|]
loadVersions :: VM ()
loadVersions = mapM_ eval [$es|
Version = Object clone
(major: Integer) . (minor: Integer) :=
Version clone do:
{ major = major
minor = minor
}
(major: Integer) . (rest: Version) :=
Version clone do:
{ major = major
minor = rest
}
(v: Version) show :=
v major show .. " . " .. v minor show
(v: Version) as: String :=
v major show .. "." .. v minor (as: String)
(s: String) as: Version :=
s (split-on: '.') (map: @(as: Integer)) reduce-right: @.
(n: Integer) as: Version := n . 0
(a: Version) == (b: Version) :=
(a major == b major) and: { a minor == b minor }
(n: Integer) == (v: Version) :=
(n == v major) && (v minor == 0)
(v: Version) == (n: Integer) :=
(n == v major) && (v minor == 0)
(a: Version) > (b: Version) :=
(a major > b major) or: { a minor > b minor }
(n: Integer) > (v: Version) :=
(n > v major) or: { v major == n && (v minor /= 0) }
(v: Version) > (n: Integer) :=
(v major > n) or: { v major == n && (v minor /= 0) }
(a: Version) < (b: Version) :=
(a major < b major) or: { a minor < b minor }
(n: Integer) < (v: Version) :=
(n < v major) or: { v major == n && (v minor /= 0) }
(v: Version) < (n: Integer) :=
(v major < n) or: { v major == n && (v minor /= 0) }
(a: Version) >= (b: Version) :=
(a major >= b major) or: { a minor >= b minor }
(n: Integer) >= (v: Version) :=
n >= v major
(v: Version) >= (n: Integer) :=
v major >= n
(a: Version) <= (b: Version) :=
(a major <= b major) or: { a minor <= b minor }
(n: Integer) <= (v: Version) :=
n <= v major
(v: Version) <= (n: Integer) :=
v major <= n
|]