atomo-0.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: ("required package not found: " .. c from .. " (satisfying " .. c to show .. ")")
-- 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 package "
d from
" (" .. d to (map: { p | p version as: String }) (join: ", ") .. ")"
" satisfy constraint "
c to show
" for package " .. pkg name show
] concat
-- 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"
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 })
}
} 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: (name: String) version: (v: Version) :=
(Eco path-to: name) </> (v as: String)
Eco executable: (name: String) :=
Directory home </> ".eco" </> "bin" </> name
Eco install: (path: String) := {
pkg = Eco Package load-from: (path </> "package.eco")
target = Eco path-to: pkg
contents = "main.atomo" . ("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 |
File copy: (path </> e to) to: (Eco executable: e from)
File set-executable: (Eco executable: e from) to: True
}
} 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)
}
} call
Eco uninstall: (name: String) :=
Directory (contents: (Eco path-to: name)) each: { v |
Eco uninstall: name version: (v as: Version)
}
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 not found: " .. name)
@(ok: []) -> raise: ("no versions for package: " .. name)
@(ok: pkgs) -> {
satisfactory = pkgs filter: { p | p version join: check }
when: satisfactory empty?
do: {
raise:
[
"no versions of package "
name
" (" .. versions (map: @(as: String)) (join: ", ") .. ")"
" satisfy constraint "
check show
] concat
}
Eco loaded << (name -> satisfactory head)
context load: ((Eco path-to: satisfactory head) </> "main.atomo")
} call
}
} call
@(ok: p) ->
when: (p version join: check) not
do: { raise: ("package " .. name .. " already loaded, but version (" .. (v as: String) .. ") does not satisfy constraint " .. check show) }
}
@ok
} call
(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
Eco Package load-from: (file: String) :=
Eco Package clone do: { load: file }
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 }
(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
|]