mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 08:54:53 +00:00
Atlas: now in its own repository (#22066)
* Atlas: now in its own repository * progress
This commit is contained in:
1345
atlas/atlas.nim
1345
atlas/atlas.nim
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
--define:ssl
|
||||
@@ -1,246 +0,0 @@
|
||||
#
|
||||
# Atlas Package Cloner
|
||||
# (c) Copyright 2021 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
##[
|
||||
|
||||
Syntax taken from strscans.nim:
|
||||
|
||||
================= ========================================================
|
||||
``$$`` Matches a single dollar sign.
|
||||
``$*`` Matches until the token following the ``$*`` was found.
|
||||
The match is allowed to be of 0 length.
|
||||
``$+`` Matches until the token following the ``$+`` was found.
|
||||
The match must consist of at least one char.
|
||||
``$s`` Skips optional whitespace.
|
||||
================= ========================================================
|
||||
|
||||
]##
|
||||
|
||||
import tables
|
||||
from strutils import continuesWith, Whitespace
|
||||
|
||||
type
|
||||
Opcode = enum
|
||||
MatchVerbatim # needs verbatim match
|
||||
Capture0Until
|
||||
Capture1Until
|
||||
Capture0UntilEnd
|
||||
Capture1UntilEnd
|
||||
SkipWhitespace
|
||||
|
||||
Instr = object
|
||||
opc: Opcode
|
||||
arg1: uint8
|
||||
arg2: uint16
|
||||
|
||||
Pattern* = object
|
||||
code: seq[Instr]
|
||||
usedMatches: int
|
||||
error: string
|
||||
|
||||
# A rewrite rule looks like:
|
||||
#
|
||||
# foo$*bar -> https://gitlab.cross.de/$1
|
||||
|
||||
proc compile*(pattern: string; strings: var seq[string]): Pattern =
|
||||
proc parseSuffix(s: string; start: int): int =
|
||||
result = start
|
||||
while result < s.len and s[result] != '$':
|
||||
inc result
|
||||
|
||||
result = Pattern(code: @[], usedMatches: 0, error: "")
|
||||
var p = 0
|
||||
while p < pattern.len:
|
||||
if pattern[p] == '$' and p+1 < pattern.len:
|
||||
case pattern[p+1]
|
||||
of '$':
|
||||
if result.code.len > 0 and result.code[^1].opc in {
|
||||
MatchVerbatim, Capture0Until, Capture1Until, Capture0UntilEnd, Capture1UntilEnd}:
|
||||
# merge with previous opcode
|
||||
let key = strings[result.code[^1].arg2] & "$"
|
||||
var idx = find(strings, key)
|
||||
if idx < 0:
|
||||
idx = strings.len
|
||||
strings.add key
|
||||
result.code[^1].arg2 = uint16(idx)
|
||||
else:
|
||||
var idx = find(strings, "$")
|
||||
if idx < 0:
|
||||
idx = strings.len
|
||||
strings.add "$"
|
||||
result.code.add Instr(opc: MatchVerbatim,
|
||||
arg1: uint8(0), arg2: uint16(idx))
|
||||
inc p, 2
|
||||
of '+', '*':
|
||||
let isPlus = pattern[p+1] == '+'
|
||||
|
||||
let pEnd = parseSuffix(pattern, p+2)
|
||||
let suffix = pattern.substr(p+2, pEnd-1)
|
||||
p = pEnd
|
||||
if suffix.len == 0:
|
||||
result.code.add Instr(opc: if isPlus: Capture1UntilEnd else: Capture0UntilEnd,
|
||||
arg1: uint8(result.usedMatches), arg2: uint16(0))
|
||||
else:
|
||||
var idx = find(strings, suffix)
|
||||
if idx < 0:
|
||||
idx = strings.len
|
||||
strings.add suffix
|
||||
result.code.add Instr(opc: if isPlus: Capture1Until else: Capture0Until,
|
||||
arg1: uint8(result.usedMatches), arg2: uint16(idx))
|
||||
inc result.usedMatches
|
||||
|
||||
of 's':
|
||||
result.code.add Instr(opc: SkipWhitespace)
|
||||
inc p, 2
|
||||
else:
|
||||
result.error = "unknown syntax '$" & pattern[p+1] & "'"
|
||||
break
|
||||
elif pattern[p] == '$':
|
||||
result.error = "unescaped '$'"
|
||||
break
|
||||
else:
|
||||
let pEnd = parseSuffix(pattern, p)
|
||||
let suffix = pattern.substr(p, pEnd-1)
|
||||
var idx = find(strings, suffix)
|
||||
if idx < 0:
|
||||
idx = strings.len
|
||||
strings.add suffix
|
||||
result.code.add Instr(opc: MatchVerbatim,
|
||||
arg1: uint8(0), arg2: uint16(idx))
|
||||
p = pEnd
|
||||
|
||||
type
|
||||
MatchObj = object
|
||||
m: int
|
||||
a: array[20, (int, int)]
|
||||
|
||||
proc matches(s: Pattern; strings: seq[string]; input: string): MatchObj =
|
||||
template failed =
|
||||
result.m = -1
|
||||
return result
|
||||
|
||||
var i = 0
|
||||
for instr in s.code:
|
||||
case instr.opc
|
||||
of MatchVerbatim:
|
||||
if continuesWith(input, strings[instr.arg2], i):
|
||||
inc i, strings[instr.arg2].len
|
||||
else:
|
||||
failed()
|
||||
of Capture0Until, Capture1Until:
|
||||
block searchLoop:
|
||||
let start = i
|
||||
while i < input.len:
|
||||
if continuesWith(input, strings[instr.arg2], i):
|
||||
if instr.opc == Capture1Until and i == start:
|
||||
failed()
|
||||
result.a[result.m] = (start, i-1)
|
||||
inc result.m
|
||||
inc i, strings[instr.arg2].len
|
||||
break searchLoop
|
||||
inc i
|
||||
failed()
|
||||
|
||||
of Capture0UntilEnd, Capture1UntilEnd:
|
||||
if instr.opc == Capture1UntilEnd and i >= input.len:
|
||||
failed()
|
||||
result.a[result.m] = (i, input.len-1)
|
||||
inc result.m
|
||||
i = input.len
|
||||
of SkipWhitespace:
|
||||
while i < input.len and input[i] in Whitespace: inc i
|
||||
if i < input.len:
|
||||
# still unmatched stuff was left:
|
||||
failed()
|
||||
|
||||
proc translate(m: MatchObj; outputPattern, input: string): string =
|
||||
result = newStringOfCap(outputPattern.len)
|
||||
var i = 0
|
||||
var patternCount = 0
|
||||
while i < outputPattern.len:
|
||||
if i+1 < outputPattern.len and outputPattern[i] == '$':
|
||||
if outputPattern[i+1] == '#':
|
||||
inc i, 2
|
||||
if patternCount < m.a.len:
|
||||
let (a, b) = m.a[patternCount]
|
||||
for j in a..b: result.add input[j]
|
||||
inc patternCount
|
||||
elif outputPattern[i+1] in {'1'..'9'}:
|
||||
var n = ord(outputPattern[i+1]) - ord('0')
|
||||
inc i, 2
|
||||
while i < outputPattern.len and outputPattern[i] in {'0'..'9'}:
|
||||
n = n * 10 + (ord(outputPattern[i]) - ord('0'))
|
||||
inc i
|
||||
patternCount = n
|
||||
if n-1 < m.a.len:
|
||||
let (a, b) = m.a[n-1]
|
||||
for j in a..b: result.add input[j]
|
||||
else:
|
||||
# just ignore the wrong pattern:
|
||||
inc i
|
||||
else:
|
||||
result.add outputPattern[i]
|
||||
inc i
|
||||
|
||||
proc replace*(s: Pattern; outputPattern, input: string): string =
|
||||
var strings: seq[string] = @[]
|
||||
let m = s.matches(strings, input)
|
||||
if m.m < 0:
|
||||
result = ""
|
||||
else:
|
||||
result = translate(m, outputPattern, input)
|
||||
|
||||
|
||||
type
|
||||
Patterns* = object
|
||||
s: seq[(Pattern, string)]
|
||||
t: Table[string, string]
|
||||
strings: seq[string]
|
||||
|
||||
proc initPatterns*(): Patterns =
|
||||
Patterns(s: @[], t: initTable[string, string](), strings: @[])
|
||||
|
||||
proc addPattern*(p: var Patterns; inputPattern, outputPattern: string): string =
|
||||
if '$' notin inputPattern and '$' notin outputPattern:
|
||||
p.t[inputPattern] = outputPattern
|
||||
result = ""
|
||||
else:
|
||||
let code = compile(inputPattern, p.strings)
|
||||
if code.error.len > 0:
|
||||
result = code.error
|
||||
else:
|
||||
p.s.add (code, outputPattern)
|
||||
result = ""
|
||||
|
||||
proc substitute*(p: Patterns; input: string): string =
|
||||
result = p.t.getOrDefault(input)
|
||||
if result.len == 0:
|
||||
for i in 0..<p.s.len:
|
||||
let m = p.s[i][0].matches(p.strings, input)
|
||||
if m.m >= 0:
|
||||
return translate(m, p.s[i][1], input)
|
||||
|
||||
proc replacePattern*(inputPattern, outputPattern, input: string): string =
|
||||
var strings: seq[string] = @[]
|
||||
let code = compile(inputPattern, strings)
|
||||
result = replace(code, outputPattern, input)
|
||||
|
||||
when isMainModule:
|
||||
# foo$*bar -> https://gitlab.cross.de/$1
|
||||
const realInput = "$fooXXbar$z00end"
|
||||
var strings: seq[string] = @[]
|
||||
let code = compile("$$foo$*bar$$$*z00$*", strings)
|
||||
echo code
|
||||
|
||||
let m = code.matches(strings, realInput)
|
||||
echo m.m
|
||||
|
||||
echo translate(m, "$1--$#-$#-", realInput)
|
||||
|
||||
echo translate(m, "https://gitlab.cross.de/$1", realInput)
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
## OS utilities like 'withDir'.
|
||||
## (c) 2021 Andreas Rumpf
|
||||
|
||||
import os, strutils, osproc
|
||||
|
||||
proc isUrl*(x: string): bool =
|
||||
x.startsWith("git://") or x.startsWith("https://") or x.startsWith("http://")
|
||||
|
||||
proc cloneUrl*(url, dest: string; cloneUsingHttps: bool): string =
|
||||
## Returns an error message on error or else "".
|
||||
result = ""
|
||||
var modUrl =
|
||||
if url.startsWith("git://") and cloneUsingHttps:
|
||||
"https://" & url[6 .. ^1]
|
||||
else: url
|
||||
|
||||
# github + https + trailing url slash causes a
|
||||
# checkout/ls-remote to fail with Repository not found
|
||||
var isGithub = false
|
||||
if modUrl.contains("github.com") and modUrl.endsWith("/"):
|
||||
modUrl = modUrl[0 .. ^2]
|
||||
isGithub = true
|
||||
|
||||
let (_, exitCode) = execCmdEx("git ls-remote --quiet --tags " & modUrl)
|
||||
var xcode = exitCode
|
||||
if isGithub and exitCode != QuitSuccess:
|
||||
# retry multiple times to avoid annoying github timeouts:
|
||||
for i in 0..4:
|
||||
os.sleep(4000)
|
||||
xcode = execCmdEx("git ls-remote --quiet --tags " & modUrl)[1]
|
||||
if xcode == QuitSuccess: break
|
||||
|
||||
if xcode == QuitSuccess:
|
||||
# retry multiple times to avoid annoying github timeouts:
|
||||
let cmd = "git clone --recursive " & modUrl & " " & dest
|
||||
for i in 0..4:
|
||||
if execShellCmd(cmd) == 0: return ""
|
||||
os.sleep(4000)
|
||||
result = "exernal program failed: " & cmd
|
||||
elif not isGithub:
|
||||
let (_, exitCode) = execCmdEx("hg identify " & modUrl)
|
||||
if exitCode == QuitSuccess:
|
||||
let cmd = "hg clone " & modUrl & " " & dest
|
||||
for i in 0..4:
|
||||
if execShellCmd(cmd) == 0: return ""
|
||||
os.sleep(4000)
|
||||
result = "exernal program failed: " & cmd
|
||||
else:
|
||||
result = "Unable to identify url: " & modUrl
|
||||
else:
|
||||
result = "Unable to identify url: " & modUrl
|
||||
@@ -1,161 +0,0 @@
|
||||
|
||||
import std / [json, os, sets, strutils, httpclient, uri]
|
||||
|
||||
type
|
||||
Package* = ref object
|
||||
# Required fields in a package.
|
||||
name*: string
|
||||
url*: string # Download location.
|
||||
license*: string
|
||||
downloadMethod*: string
|
||||
description*: string
|
||||
tags*: seq[string] # \
|
||||
# From here on, optional fields set to the empty string if not available.
|
||||
version*: string
|
||||
dvcsTag*: string
|
||||
web*: string # Info url for humans.
|
||||
|
||||
proc optionalField(obj: JsonNode, name: string, default = ""): string =
|
||||
if hasKey(obj, name) and obj[name].kind == JString:
|
||||
result = obj[name].str
|
||||
else:
|
||||
result = default
|
||||
|
||||
proc requiredField(obj: JsonNode, name: string): string =
|
||||
result = optionalField(obj, name, "")
|
||||
|
||||
proc fromJson*(obj: JSonNode): Package =
|
||||
result = Package()
|
||||
result.name = obj.requiredField("name")
|
||||
if result.name.len == 0: return nil
|
||||
result.version = obj.optionalField("version")
|
||||
result.url = obj.requiredField("url")
|
||||
if result.url.len == 0: return nil
|
||||
result.downloadMethod = obj.requiredField("method")
|
||||
if result.downloadMethod.len == 0: return nil
|
||||
result.dvcsTag = obj.optionalField("dvcs-tag")
|
||||
result.license = obj.optionalField("license")
|
||||
result.tags = @[]
|
||||
for t in obj["tags"]:
|
||||
result.tags.add(t.str)
|
||||
result.description = obj.requiredField("description")
|
||||
result.web = obj.optionalField("web")
|
||||
|
||||
const PackagesDir* = "packages"
|
||||
|
||||
proc getPackages*(workspaceDir: string): seq[Package] =
|
||||
result = @[]
|
||||
var uniqueNames = initHashSet[string]()
|
||||
var jsonFiles = 0
|
||||
for kind, path in walkDir(workspaceDir / PackagesDir):
|
||||
if kind == pcFile and path.endsWith(".json"):
|
||||
inc jsonFiles
|
||||
let packages = json.parseFile(path)
|
||||
for p in packages:
|
||||
let pkg = p.fromJson()
|
||||
if pkg != nil and not uniqueNames.containsOrIncl(pkg.name):
|
||||
result.add(pkg)
|
||||
|
||||
proc `$`*(pkg: Package): string =
|
||||
result = pkg.name & ":\n"
|
||||
result &= " url: " & pkg.url & " (" & pkg.downloadMethod & ")\n"
|
||||
result &= " tags: " & pkg.tags.join(", ") & "\n"
|
||||
result &= " description: " & pkg.description & "\n"
|
||||
result &= " license: " & pkg.license & "\n"
|
||||
if pkg.web.len > 0:
|
||||
result &= " website: " & pkg.web & "\n"
|
||||
|
||||
proc toTags(j: JsonNode): seq[string] =
|
||||
result = @[]
|
||||
if j.kind == JArray:
|
||||
for elem in items j:
|
||||
result.add elem.getStr("")
|
||||
|
||||
proc singleGithubSearch(term: string): JsonNode =
|
||||
# For example:
|
||||
# https://api.github.com/search/repositories?q=weave+language:nim
|
||||
var client = newHttpClient()
|
||||
try:
|
||||
let x = client.getContent("https://api.github.com/search/repositories?q=" & encodeUrl(term) & "+language:nim")
|
||||
result = parseJson(x)
|
||||
except:
|
||||
result = parseJson("{\"items\": []}")
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
proc githubSearch(seen: var HashSet[string]; terms: seq[string]) =
|
||||
for term in terms:
|
||||
let results = singleGithubSearch(term)
|
||||
for j in items(results.getOrDefault("items")):
|
||||
let p = Package(
|
||||
name: j.getOrDefault("name").getStr,
|
||||
url: j.getOrDefault("html_url").getStr,
|
||||
downloadMethod: "git",
|
||||
tags: toTags(j.getOrDefault("topics")),
|
||||
description: j.getOrDefault("description").getStr,
|
||||
license: j.getOrDefault("license").getOrDefault("spdx_id").getStr,
|
||||
web: j.getOrDefault("html_url").getStr
|
||||
)
|
||||
if not seen.containsOrIncl(p.url):
|
||||
echo p
|
||||
|
||||
proc getUrlFromGithub*(term: string): string =
|
||||
let results = singleGithubSearch(term)
|
||||
var matches = 0
|
||||
result = ""
|
||||
for j in items(results.getOrDefault("items")):
|
||||
if cmpIgnoreCase(j.getOrDefault("name").getStr, term) == 0:
|
||||
if matches == 0:
|
||||
result = j.getOrDefault("html_url").getStr
|
||||
inc matches
|
||||
if matches != 1:
|
||||
# ambiguous, not ok!
|
||||
result = ""
|
||||
|
||||
proc search*(pkgList: seq[Package]; terms: seq[string]) =
|
||||
var seen = initHashSet[string]()
|
||||
template onFound =
|
||||
echo pkg
|
||||
seen.incl pkg.url
|
||||
break forPackage
|
||||
|
||||
for pkg in pkgList:
|
||||
if terms.len > 0:
|
||||
block forPackage:
|
||||
for term in terms:
|
||||
let word = term.toLower
|
||||
# Search by name.
|
||||
if word in pkg.name.toLower:
|
||||
onFound()
|
||||
# Search by tag.
|
||||
for tag in pkg.tags:
|
||||
if word in tag.toLower:
|
||||
onFound()
|
||||
else:
|
||||
echo(pkg)
|
||||
githubSearch seen, terms
|
||||
if seen.len == 0 and terms.len > 0:
|
||||
echo("No package found.")
|
||||
|
||||
type PkgCandidates* = array[3, seq[Package]]
|
||||
|
||||
proc determineCandidates*(pkgList: seq[Package];
|
||||
terms: seq[string]): PkgCandidates =
|
||||
result[0] = @[]
|
||||
result[1] = @[]
|
||||
result[2] = @[]
|
||||
for pkg in pkgList:
|
||||
block termLoop:
|
||||
for term in terms:
|
||||
let word = term.toLower
|
||||
if word == pkg.name.toLower:
|
||||
result[0].add pkg
|
||||
break termLoop
|
||||
elif word in pkg.name.toLower:
|
||||
result[1].add pkg
|
||||
break termLoop
|
||||
else:
|
||||
for tag in pkg.tags:
|
||||
if word in tag.toLower:
|
||||
result[2].add pkg
|
||||
break termLoop
|
||||
313
atlas/sat.nim
313
atlas/sat.nim
@@ -1,313 +0,0 @@
|
||||
## SAT solver
|
||||
## (c) 2021 Andreas Rumpf
|
||||
## Based on explanations and Haskell code from
|
||||
## https://andrew.gibiansky.com/blog/verification/writing-a-sat-solver/
|
||||
|
||||
## Formulars as packed ASTs, no pointers no cry. Solves formulars with many
|
||||
## thousands of variables in no time.
|
||||
|
||||
type
|
||||
FormKind* = enum
|
||||
FalseForm, TrueForm, VarForm, NotForm, AndForm, OrForm, ExactlyOneOfForm # roughly 8 so the last 3 bits
|
||||
BaseType = int32
|
||||
Atom = distinct BaseType
|
||||
VarId* = distinct BaseType
|
||||
Formular* = seq[Atom] # linear storage
|
||||
|
||||
proc `==`*(a, b: VarId): bool {.borrow.}
|
||||
|
||||
const
|
||||
KindBits = 3
|
||||
KindMask = 0b111
|
||||
|
||||
template kind(a: Atom): FormKind = FormKind(BaseType(a) and KindMask)
|
||||
template intVal(a: Atom): BaseType = BaseType(a) shr KindBits
|
||||
|
||||
proc newVar*(val: VarId): Atom {.inline.} =
|
||||
Atom((BaseType(val) shl KindBits) or BaseType(VarForm))
|
||||
|
||||
proc newOperation(k: FormKind; val: BaseType): Atom {.inline.} =
|
||||
Atom((val shl KindBits) or BaseType(k))
|
||||
|
||||
proc trueLit(): Atom {.inline.} = Atom(TrueForm)
|
||||
proc falseLit(): Atom {.inline.} = Atom(FalseForm)
|
||||
|
||||
proc lit(k: FormKind): Atom {.inline.} = Atom(k)
|
||||
|
||||
when false:
|
||||
proc isTrueLit(a: Atom): bool {.inline.} = a.kind == TrueForm
|
||||
proc isFalseLit(a: Atom): bool {.inline.} = a.kind == FalseForm
|
||||
|
||||
proc varId(a: Atom): VarId =
|
||||
assert a.kind == VarForm
|
||||
result = VarId(BaseType(a) shr KindBits)
|
||||
|
||||
type
|
||||
PatchPos = distinct int
|
||||
FormPos = distinct int
|
||||
|
||||
proc prepare(dest: var Formular; source: Formular; sourcePos: FormPos): PatchPos =
|
||||
result = PatchPos dest.len
|
||||
dest.add source[sourcePos.int]
|
||||
|
||||
proc patch(f: var Formular; pos: PatchPos) =
|
||||
let pos = pos.int
|
||||
let k = f[pos].kind
|
||||
assert k > VarForm
|
||||
let distance = int32(f.len - pos)
|
||||
f[pos] = newOperation(k, distance)
|
||||
|
||||
proc nextChild(f: Formular; pos: var int) {.inline.} =
|
||||
let x = f[int pos]
|
||||
pos += (if x.kind <= VarForm: 1 else: int(intVal(x)))
|
||||
|
||||
iterator sonsReadonly(f: Formular; n: FormPos): FormPos =
|
||||
var pos = n.int
|
||||
assert f[pos].kind > VarForm
|
||||
let last = pos + f[pos].intVal
|
||||
inc pos
|
||||
while pos < last:
|
||||
yield FormPos pos
|
||||
nextChild f, pos
|
||||
|
||||
iterator sons(dest: var Formular; source: Formular; n: FormPos): FormPos =
|
||||
let patchPos = prepare(dest, source, n)
|
||||
for x in sonsReadonly(source, n): yield x
|
||||
patch dest, patchPos
|
||||
|
||||
# String representation
|
||||
|
||||
proc toString(dest: var string; f: Formular; n: FormPos) =
|
||||
assert n.int >= 0
|
||||
assert n.int < f.len
|
||||
case f[n.int].kind
|
||||
of FalseForm: dest.add 'F'
|
||||
of TrueForm: dest.add 'T'
|
||||
of VarForm:
|
||||
dest.add 'v'
|
||||
dest.addInt varId(f[n.int]).int
|
||||
else:
|
||||
case f[n.int].kind
|
||||
of AndForm:
|
||||
dest.add "(&"
|
||||
of OrForm:
|
||||
dest.add "(|"
|
||||
of ExactlyOneOfForm:
|
||||
dest.add "(1=="
|
||||
of NotForm:
|
||||
dest.add "(~"
|
||||
else: assert false, "cannot happen"
|
||||
for child in sonsReadonly(f, n):
|
||||
toString(dest, f, child)
|
||||
dest.add ' '
|
||||
dest[^1] = ')'
|
||||
|
||||
proc `$`*(f: Formular): string =
|
||||
assert f.len > 0
|
||||
toString(result, f, FormPos 0)
|
||||
|
||||
type
|
||||
Builder* = object
|
||||
f: Formular
|
||||
toPatch: seq[PatchPos]
|
||||
|
||||
proc isEmpty*(b: Builder): bool {.inline.} =
|
||||
b.f.len == 0 or b.f.len == 1 and b.f[0].kind in {NotForm, AndForm, OrForm, ExactlyOneOfForm}
|
||||
|
||||
proc openOpr*(b: var Builder; k: FormKind) =
|
||||
b.toPatch.add PatchPos b.f.len
|
||||
b.f.add newOperation(k, 0)
|
||||
|
||||
proc add*(b: var Builder; a: Atom) =
|
||||
b.f.add a
|
||||
|
||||
proc closeOpr*(b: var Builder) =
|
||||
patch(b.f, b.toPatch.pop())
|
||||
|
||||
proc toForm*(b: var Builder): Formular =
|
||||
assert b.toPatch.len == 0, "missing `closeOpr` calls"
|
||||
result = move b.f
|
||||
|
||||
# Code from the blog translated into Nim and into our representation
|
||||
|
||||
const
|
||||
NoVar = VarId(-1)
|
||||
|
||||
proc freeVariable(f: Formular): VarId =
|
||||
## returns NoVar if there is no free variable.
|
||||
for i in 0..<f.len:
|
||||
if f[i].kind == VarForm: return varId(f[i])
|
||||
return NoVar
|
||||
|
||||
type
|
||||
BindingKind* = enum
|
||||
dontCare,
|
||||
setToFalse,
|
||||
setToTrue
|
||||
Solution* = seq[BindingKind]
|
||||
|
||||
proc simplify(dest: var Formular; source: Formular; n: FormPos; sol: Solution): FormKind =
|
||||
## Returns either a Const constructor or a simplified expression;
|
||||
## if the result is not a Const constructor, it guarantees that there
|
||||
## are no Const constructors in the source tree further down.
|
||||
let s = source[n.int]
|
||||
result = s.kind
|
||||
case result
|
||||
of FalseForm, TrueForm:
|
||||
# nothing interesting to do:
|
||||
dest.add s
|
||||
of VarForm:
|
||||
let v = varId(s).int
|
||||
if v < sol.len:
|
||||
case sol[v]
|
||||
of dontCare:
|
||||
dest.add s
|
||||
of setToFalse:
|
||||
dest.add falseLit()
|
||||
result = FalseForm
|
||||
of setToTrue:
|
||||
dest.add trueLit()
|
||||
result = TrueForm
|
||||
else:
|
||||
dest.add s
|
||||
of NotForm:
|
||||
let oldLen = dest.len
|
||||
var inner: FormKind
|
||||
for child in sons(dest, source, n):
|
||||
inner = simplify(dest, source, child, sol)
|
||||
if inner in {FalseForm, TrueForm}:
|
||||
setLen dest, oldLen
|
||||
result = (if inner == FalseForm: TrueForm else: FalseForm)
|
||||
dest.add lit(result)
|
||||
of AndForm, OrForm:
|
||||
let (tForm, fForm) = if result == AndForm: (TrueForm, FalseForm)
|
||||
else: (FalseForm, TrueForm)
|
||||
|
||||
let initialLen = dest.len
|
||||
var childCount = 0
|
||||
for child in sons(dest, source, n):
|
||||
let oldLen = dest.len
|
||||
|
||||
let inner = simplify(dest, source, child, sol)
|
||||
# ignore 'and T' or 'or F' subexpressions:
|
||||
if inner == tForm:
|
||||
setLen dest, oldLen
|
||||
elif inner == fForm:
|
||||
# 'and F' is always false and 'or T' is always true:
|
||||
result = fForm
|
||||
break
|
||||
else:
|
||||
inc childCount
|
||||
|
||||
if result == fForm:
|
||||
setLen dest, initialLen
|
||||
dest.add lit(result)
|
||||
elif childCount == 1:
|
||||
for i in initialLen..<dest.len-1:
|
||||
dest[i] = dest[i+1]
|
||||
setLen dest, dest.len-1
|
||||
result = dest[initialLen].kind
|
||||
elif childCount == 0:
|
||||
# that means all subexpressions where ignored:
|
||||
setLen dest, initialLen
|
||||
result = tForm
|
||||
dest.add lit(result)
|
||||
of ExactlyOneOfForm:
|
||||
let initialLen = dest.len
|
||||
var childCount = 0
|
||||
var couldEval = 0
|
||||
for child in sons(dest, source, n):
|
||||
let oldLen = dest.len
|
||||
|
||||
let inner = simplify(dest, source, child, sol)
|
||||
# ignore 'exactlyOneOf F' subexpressions:
|
||||
if inner == FalseForm:
|
||||
setLen dest, oldLen
|
||||
else:
|
||||
if inner == TrueForm:
|
||||
inc couldEval
|
||||
inc childCount
|
||||
|
||||
if couldEval == childCount:
|
||||
setLen dest, initialLen
|
||||
if couldEval != 1:
|
||||
dest.add lit FalseForm
|
||||
else:
|
||||
dest.add lit TrueForm
|
||||
elif childCount == 1:
|
||||
for i in initialLen..<dest.len-1:
|
||||
dest[i] = dest[i+1]
|
||||
setLen dest, dest.len-1
|
||||
result = dest[initialLen].kind
|
||||
|
||||
proc satisfiable*(f: Formular; s: var Solution): bool =
|
||||
let v = freeVariable(f)
|
||||
if v == NoVar:
|
||||
result = f[0].kind == TrueForm
|
||||
else:
|
||||
result = false
|
||||
# We have a variable to guess.
|
||||
# Construct the two guesses.
|
||||
# Return whether either one of them works.
|
||||
if v.int >= s.len: s.setLen v.int+1
|
||||
# try `setToFalse` first so that we don't end up with unnecessary dependencies:
|
||||
s[v.int] = setToFalse
|
||||
|
||||
var falseGuess: Formular
|
||||
let res = simplify(falseGuess, f, FormPos 0, s)
|
||||
|
||||
if res == TrueForm:
|
||||
result = true
|
||||
else:
|
||||
result = satisfiable(falseGuess, s)
|
||||
if not result:
|
||||
s[v.int] = setToTrue
|
||||
|
||||
var trueGuess: Formular
|
||||
let res = simplify(trueGuess, f, FormPos 0, s)
|
||||
|
||||
if res == TrueForm:
|
||||
result = true
|
||||
else:
|
||||
result = satisfiable(trueGuess, s)
|
||||
if not result:
|
||||
# heuristic that provides a solution that comes closest to the "real" conflict:
|
||||
s[v.int] = if trueGuess.len <= falseGuess.len: setToFalse else: setToTrue
|
||||
|
||||
when isMainModule:
|
||||
proc main =
|
||||
var b: Builder
|
||||
b.openOpr(AndForm)
|
||||
|
||||
b.openOpr(OrForm)
|
||||
b.add newVar(VarId 1)
|
||||
b.add newVar(VarId 2)
|
||||
b.add newVar(VarId 3)
|
||||
b.add newVar(VarId 4)
|
||||
b.closeOpr
|
||||
|
||||
b.openOpr(ExactlyOneOfForm)
|
||||
b.add newVar(VarId 5)
|
||||
b.add newVar(VarId 6)
|
||||
b.add newVar(VarId 7)
|
||||
|
||||
#b.openOpr(NotForm)
|
||||
b.add newVar(VarId 8)
|
||||
#b.closeOpr
|
||||
b.closeOpr
|
||||
|
||||
b.add newVar(VarId 5)
|
||||
b.add newVar(VarId 6)
|
||||
b.closeOpr
|
||||
|
||||
let f = toForm(b)
|
||||
echo "original: "
|
||||
echo f
|
||||
|
||||
var s: Solution
|
||||
echo satisfiable(f, s)
|
||||
echo "solution"
|
||||
for i in 0..<s.len:
|
||||
echo "v", i, " ", s[i]
|
||||
|
||||
main()
|
||||
@@ -1,63 +0,0 @@
|
||||
|
||||
type
|
||||
PerDirData = object
|
||||
dirname: string
|
||||
cmd: Command
|
||||
exitCode: int
|
||||
output: string
|
||||
|
||||
template toData(a, b, c, d): untyped =
|
||||
PerDirData(dirname: a, cmd: b, exitCode: c, output: d)
|
||||
|
||||
const
|
||||
TestLog = [
|
||||
toData("balls", GitPull, 0, "Already up to date.\n"),
|
||||
toData("grok", GitDiff, 0, ""),
|
||||
toData("grok", GitTags, 0, "2ca193c31fa2377c1e991a080d60ca3215ff6cf0 refs/tags/0.0.1\n48007554b21ba2f65c726ae2fdda88d621865b4a refs/tags/0.0.2\n7092a0286421c7818cd335cca9ebc72d03d866c2 refs/tags/0.0.3\n62707b8ac684efac35d301dbde57dc750880268e refs/tags/0.0.4\n876f2504e0c2f785ffd2cf65a78e2aea474fa8aa refs/tags/0.0.5\nb7eb1f2501aa2382cb3a38353664a13af62a9888 refs/tags/0.0.6\nf5d818bfd6038884b3d8b531c58484ded20a58a4 refs/tags/0.1.0\n961eaddea49c3144d130d105195583d3f11fb6c6 refs/tags/0.2.0\n15ab8ed8d4f896232a976a9008548bd53af72a66 refs/tags/0.2.1\n426a7d7d4603f77ced658e73ad7f3f582413f6cd refs/tags/0.3.0\n83cf7a39b2fe897786fb0fe01a7a5933c3add286 refs/tags/0.3.1\n8d2e3c900edbc95fa0c036fd76f8e4f814aef2c1 refs/tags/0.3.2\n48b43372f49a3bb4dc0969d82a0fca183fb94662 refs/tags/0.3.3\n9ca947a3009ea6ba17814b20eb953272064eb2e6 refs/tags/0.4.0\n1b5643d04fba6d996a16d1ffc13d034a40003f8f refs/tags/0.5.0\n486b0eb580b1c465453d264ac758cc490c19c33e refs/tags/0.5.1\naedb0d9497390e20b9d2541cef2bb05a5cda7a71 refs/tags/0.5.2\n"),
|
||||
toData("grok", GitCurrentCommit, 0, "349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"),
|
||||
toData("grok", GitMergeBase, 0, "349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n1b5643d04fba6d996a16d1ffc13d034a40003f8f\n349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"),
|
||||
toData("grok", GitCheckout, 0, "1b5643d04fba6d996a16d1ffc13d034a40003f8f"), # watch out!
|
||||
|
||||
toData("ups", GitDiff, 0, ""),
|
||||
toData("ups", GitTags, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1 refs/tags/0.0.2\n19bc490c22b4f5b0628c31cdedead1375b279356 refs/tags/0.0.3\nff34602aaea824cb46d6588cd5fe1178132e9702 refs/tags/0.0.4\n09de599138f20b745133b6e4fe563e204415a7e8 refs/tags/0.0.5\n85fee3b74798311108a105635df31f892150f5d0 refs/tags/0.0.6\nfd303913b22b121dc42f332109e9c44950b9acd4 refs/tags/0.0.7\n"),
|
||||
toData("ups", GitCurrentCommit, 0, "74c31af8030112dac758440aa51ef175992f71f3\n"),
|
||||
toData("ups", GitMergeBase, 0, "74c31af8030112dac758440aa51ef175992f71f3\n4008f9339cd22b30e180bc87a6cca7270fd28ac1\n74c31af8030112dac758440aa51ef175992f71f3\n"),
|
||||
toData("ups", GitCheckout, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1"),
|
||||
|
||||
toData("sync", GitDiff, 0, ""),
|
||||
toData("sync", GitRevParse, 0, "810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"),
|
||||
toData("sync", GitCurrentCommit, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n"),
|
||||
toData("sync", GitMergeBase, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"),
|
||||
|
||||
toData("npeg", GitDiff, 0, ""),
|
||||
toData("npeg", GitTags, 0, "8df2f0c9391995fd086b8aab00e8ab7aded1e8f0 refs/tags/0.1.0\n4c959a72db5283b55eeef491076eefb5e02316f1 refs/tags/0.10.0\n802f47c0f7f4318a4f0858ba5a6a6ed2333bde71 refs/tags/0.11.0\n82c8d92837108dce225358ace2c416bf9a3f30ce refs/tags/0.12.0\n87d2f2c4f6ef7da350d45beb5a336611bde7f518 refs/tags/0.13.0\n39964f0d220bfaade47a568bf03c1cf28aa2bc37 refs/tags/0.14.0\nbe9f03f92304cbeab70572944a8563db9b23b2fb refs/tags/0.14.1\na933fb9832566fc95273e417597bfb4faf564ca6 refs/tags/0.15.0\n6aad2e438c52ff0636c7bfb64338e444ac3e83ba refs/tags/0.16.0\nf4ddffb5848c42c6151743dd9c7eddcaaabc56cc refs/tags/0.17.0\n30b446b39442cdbc53a97018ab8a54149aa7c3b7 refs/tags/0.17.1\n1a9d36aa3b34a6169d4530463f1c17a3fe1e075e refs/tags/0.18.0\ndd34f903a9a63b876cb2db19b7a4ce0bcc252134 refs/tags/0.19.0\nd93d49c81fc8722d7929ac463b435c0f2e10c53b refs/tags/0.2.0\neeae7746c9b1118bcf27744ab2aee26969051256 refs/tags/0.20.0\n8c3471a548129f3bf62df15cd0fd8cca1787d852 refs/tags/0.21.0\nc0e873a17bc713c80e74fec3c30cb62dcd5d194a refs/tags/0.21.1\nbae84c47a1bb259b209b6f6be1582327b784539d refs/tags/0.21.2\nbfcb4bcae76a917c3c88736ca773e4cb67dbb2d8 refs/tags/0.21.3\n0eabb7c462d30932049f0b7e6a030c1562cf9fee refs/tags/0.22.0\n2e75367095f54d4351005078bad98041a55b14c1 refs/tags/0.22.1\n814ea235dd398108d7b18f966694c3d951575701 refs/tags/0.22.2\na812064587d983c129737f8500bf74990e6b8dab refs/tags/0.23.0\nbd969ad3745db0d66022564cac76cf9424651104 refs/tags/0.23.1\na037c646a47623b92718efadc2bb74d03664b360 refs/tags/0.23.2\n078475ccceeaca0fac947492acdd24514da8d863 refs/tags/0.24.0\ne7bd87dc992512fd5825a557a56907647e03c979 refs/tags/0.24.1\n45ea601e1c7f64fb857bc99df984b86673621d2c refs/tags/0.3.0\n1ea9868a3fee3aa487ab7ec9129208a4dd483d0d refs/tags/0.4.0\n39afdb5733d3245386d29d08c5ff61c89268f499 refs/tags/0.5.0\n458c7b5910fcb157af3fc51bc3b3e663fdb3ed4a refs/tags/0.6.0\n06c38bd8563d822455bc237c2a98c153d938ed1b refs/tags/0.7.0\nf446b6056eef6d8dc9d8b47a79aca93d17dc8230 refs/tags/0.8.0\nbb25a195133f9f7af06386d0809793923cc5e8ab refs/tags/0.9.0\n"),
|
||||
toData("npeg", GitCurrentCommit, 0, "5d80f93aa720898936668b3bc47d0fff101ec414\n"),
|
||||
toData("npeg", GitMergeBase, 0, "5d80f93aa720898936668b3bc47d0fff101ec414\na037c646a47623b92718efadc2bb74d03664b360\na037c646a47623b92718efadc2bb74d03664b360\n"),
|
||||
|
||||
toData("testes", GitDiff, 0, ""),
|
||||
toData("testes", GitTags, 0, "3ce9b2968b5f644755a0ced1baa3eece88c2f12e refs/tags/0.1.0\nf73af8318b54737678fab8b54bdcd8a451015e0d refs/tags/0.1.1\nd21d84d37b161a123a43318bae353108755916de refs/tags/0.1.2\n5c36b6095353ed03b08ac939d00aff2d73f79a35 refs/tags/0.1.3\na1220d11237ee8f135f772ff9731c11b2d91ba31 refs/tags/0.1.4\n574f741b90d04a7ce8c9b990e6077708d7ad076e refs/tags/0.1.5\nced0a9e58234b680def6931578e09165a32e6291 refs/tags/0.1.6\nbb248952e8742a6011eb1a45a9d2059aeb0341d7 refs/tags/0.1.7\nabb7d7c552da0a8e0ddc586c15ccf7e74b0d068b refs/tags/0.10.0\n6e42a768a90d6442196b344bcdcb6f834b76e7b7 refs/tags/0.2.0\n9d136c3a0851ca2c021f5fb4f7b63f0a0ef77232 refs/tags/0.2.1\ndcb282b2da863fd2939e1969cec7a99788feb456 refs/tags/0.2.2\nf708a632afaa40a322a1a61c1c13722edac8e8c5 refs/tags/0.3.0\n3213f59e3f9ba052452c59f01d1418360d856af6 refs/tags/0.3.1\nf7bb1743dffd327958dfcebae4cfb6f61cc1cb8c refs/tags/0.3.2\n6b64569ebecad6bc60cc8697713701e7659204f4 refs/tags/0.3.3\nb51c25a4367bd17f419f78cb5a27f319e9d820f5 refs/tags/0.3.4\nb265612710cbd5ddb1b173c94ece8ec5c7ceccac refs/tags/0.3.5\ne404bcfe42e92d7509717a2dfa115cacb4964c5d refs/tags/0.3.6\n5e4d0d5b7e7f314dde701c546c4365c59782d3dc refs/tags/0.3.7\ne13f91c9c913d2b81c59adeaad687efa2b35293a refs/tags/0.3.8\n17599625f09af0ae4b525e63ab726a3002540702 refs/tags/0.3.9\n13e907f70571dd146d8dc29ddec4599b40ba4e85 refs/tags/0.4.0\n155a74cf676495df1e0674dd07b5e4a0291a9a4a refs/tags/0.4.1\nf37abccdc148cb02ca637a6f0bc8821491cce358 refs/tags/0.4.2\n0250d29ebdd02f28f9020445adb5a4e51fd1902c refs/tags/0.5.0\n2fb87db6d9f34109a70205876030c53f815739b7 refs/tags/0.5.1\n629d17ba8d6a1a4eca8145eb089ed5bca4473dfc refs/tags/0.6.0\ne926130f5f1b7903f68be49cc1563225bd9d948d refs/tags/0.7.0\n7365303897e6185796c274425c079916047e3f14 refs/tags/0.7.1\na735c4adabeba637409f41c4325dd8fc5fb91e2d refs/tags/0.7.10\nfe023fd27404889c5122f902456cbba14b767405 refs/tags/0.7.11\n4430e72972c77a5e9c1555d59bba11d840682691 refs/tags/0.7.12\nf0e53eb490a9558c7f594d2e095b70665e36ca88 refs/tags/0.7.13\nf6520e25e7c329c2957cda447f149fc6a930db0d refs/tags/0.7.2\nd509762f7191757c240d3c79c9ecda53f8c0cfe3 refs/tags/0.7.3\nc02e7a783d1c42fd1f91bca7142f7c3733950c05 refs/tags/0.7.4\n8c8a9e496e9b86ba7602709438980ca31e6989d9 refs/tags/0.7.5\n29839c18b4ac83c0111a178322b57ebb8a8d402c refs/tags/0.7.6\n3b62973cf74fafd8ea906644d89ac34d29a8a6cf refs/tags/0.7.7\ne67ff99dc43c391e89a37f97a9d298c3428bbde2 refs/tags/0.7.8\n4b72ecda0d40ed8e5ab8ad4095a0691d30ec6cd0 refs/tags/0.7.9\n2512b8cc3d7f001d277e89978da2049a5feee5c4 refs/tags/0.8.0\n86c47029690bd2731d204245f3f54462227bba0d refs/tags/0.9.0\n9a7f94f78588e9b5ba7ca077e1f7eae0607c6cf6 refs/tags/0.9.1\n08c915dc016d16c1dfa9a77d0b045ec29c9f2074 refs/tags/0.9.2\n3fb658b1ce1e1efa37d6f9f14322bdac8def02a5 refs/tags/0.9.3\n738fda0add962379ffe6aa6ca5f01a6943a98a2e refs/tags/0.9.4\n48d821add361f7ad768ecb35a0b19c38f90c919e refs/tags/0.9.5\nff9ae890f597dac301b2ac6e6805eb9ac5afd49a refs/tags/0.9.6\n483c78f06e60b0ec5e79fc3476df075ee7286890 refs/tags/0.9.7\n416eec87a5ae39a1a6035552e9e9a47d76b13026 refs/tags/1.0.0\na935cfe9445cc5218fbdd7e0afb35aa1587fff61 refs/tags/1.0.1\n4b83863a9181f054bb695b11b5d663406dfd85d2 refs/tags/1.0.2\n295145fddaa4fe29c1e71a5044d968a84f9dbf69 refs/tags/1.1.0\n8f74ea4e5718436c47305b4488842e6458a13dac refs/tags/1.1.1\n4135bb291e53d615a976e997c44fb2bd9e1ad343 refs/tags/1.1.10\n8c09dbcd16612f5989065db02ea2e7a752dd2656 refs/tags/1.1.11\naedfebdb6c016431d84b0c07cf181b957a900640 refs/tags/1.1.12\n2c2e958366ef6998115740bdf110588d730e5738 refs/tags/1.1.2\nbecc77258321e6ec40d89efdddf37bafd0d07fc3 refs/tags/1.1.3\ne070d7c9853bf94c35b81cf0c0a8980c2449bb22 refs/tags/1.1.4\n12c986cbbf65e8571a486e9230808bf887e5f04f refs/tags/1.1.5\n63df8986f5b56913b02d26954fa033eeaf43714c refs/tags/1.1.6\n38e02c9c6bd728b043036fe0d1894d774cab3108 refs/tags/1.1.7\n3c3879fff16450d28ade79a6b08982bf5cefc061 refs/tags/1.1.8\ne32b811b3b2e70a1d189d7a663bc2583e9c18f96 refs/tags/1.1.9\n0c1b4277c08197ce7e7e0aa2bad91d909fcd96ac refs/tags/2.0.0\n"),
|
||||
toData("testes", GitCurrentCommit, 0, "d9db2ad09aa38fc26625341e1b666602959e144f\n"),
|
||||
toData("testes", GitMergeBase, 0, "d9db2ad09aa38fc26625341e1b666602959e144f\n416eec87a5ae39a1a6035552e9e9a47d76b13026\nd9db2ad09aa38fc26625341e1b666602959e144f\n"),
|
||||
toData("testes", GitCheckout, 0, "416eec87a5ae39a1a6035552e9e9a47d76b13026"),
|
||||
|
||||
toData("grok", GitDiff, 0, ""),
|
||||
toData("grok", GitTags, 0, "2ca193c31fa2377c1e991a080d60ca3215ff6cf0 refs/tags/0.0.1\n48007554b21ba2f65c726ae2fdda88d621865b4a refs/tags/0.0.2\n7092a0286421c7818cd335cca9ebc72d03d866c2 refs/tags/0.0.3\n62707b8ac684efac35d301dbde57dc750880268e refs/tags/0.0.4\n876f2504e0c2f785ffd2cf65a78e2aea474fa8aa refs/tags/0.0.5\nb7eb1f2501aa2382cb3a38353664a13af62a9888 refs/tags/0.0.6\nf5d818bfd6038884b3d8b531c58484ded20a58a4 refs/tags/0.1.0\n961eaddea49c3144d130d105195583d3f11fb6c6 refs/tags/0.2.0\n15ab8ed8d4f896232a976a9008548bd53af72a66 refs/tags/0.2.1\n426a7d7d4603f77ced658e73ad7f3f582413f6cd refs/tags/0.3.0\n83cf7a39b2fe897786fb0fe01a7a5933c3add286 refs/tags/0.3.1\n8d2e3c900edbc95fa0c036fd76f8e4f814aef2c1 refs/tags/0.3.2\n48b43372f49a3bb4dc0969d82a0fca183fb94662 refs/tags/0.3.3\n9ca947a3009ea6ba17814b20eb953272064eb2e6 refs/tags/0.4.0\n1b5643d04fba6d996a16d1ffc13d034a40003f8f refs/tags/0.5.0\n486b0eb580b1c465453d264ac758cc490c19c33e refs/tags/0.5.1\naedb0d9497390e20b9d2541cef2bb05a5cda7a71 refs/tags/0.5.2\n"),
|
||||
toData("grok", GitCurrentCommit, 0, "4e6526a91a23eaec778184e16ce9a34d25d48bdc\n"),
|
||||
toData("grok", GitMergeBase, 0, "4e6526a91a23eaec778184e16ce9a34d25d48bdc\n62707b8ac684efac35d301dbde57dc750880268e\n349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"),
|
||||
toData("grok", GitCheckout, 0, "62707b8ac684efac35d301dbde57dc750880268e"),
|
||||
|
||||
toData("nim-bytes2human", GitDiff, 0, ""),
|
||||
toData("nim-bytes2human", GitTags, 0, ""),
|
||||
toData("nim-bytes2human", GitCurrentcommit, 0, "ec2c1a758cabdd4751a06c8ebf2b923f19e32731\n")
|
||||
]
|
||||
|
||||
#[
|
||||
Current directory is now E:\atlastest\nim-bytes2human
|
||||
cmd git diff args [] --> ("", 0)
|
||||
cmd git show-ref --tags args [] --> ("", 1)
|
||||
cmd git log -n 1 --format=%H args [] --> (, 0)
|
||||
[Warning] (nim-bytes2human) package has no tagged releases
|
||||
nimble E:\atlastest\nim-bytes2human\bytes2human.nimble info (requires: @["nim >= 1.0.0"], srcDir: "src", tasks: @[])
|
||||
[Error] There were problems.
|
||||
Error: execution of an external program failed: 'E:\nim\tools\atlas\atlas.exe clone https://github.com/disruptek/balls'
|
||||
]#
|
||||
@@ -1,46 +0,0 @@
|
||||
# Small program that runs the test cases
|
||||
|
||||
import std / [strutils, os, sequtils]
|
||||
from std/private/gitutils import diffFiles
|
||||
|
||||
if execShellCmd("nim c -r atlas/versions.nim") != 0:
|
||||
quit("FAILURE: unit tests in atlas/versions.nim failed")
|
||||
|
||||
var failures = 0
|
||||
|
||||
when defined(develop):
|
||||
const atlasExe = "bin" / "atlas".addFileExt(ExeExt)
|
||||
if execShellCmd("nim c -o:$# atlas/atlas.nim" % [atlasExe]) != 0:
|
||||
quit("FAILURE: compilation of atlas failed")
|
||||
else:
|
||||
const atlasExe = "atlas".addFileExt(ExeExt)
|
||||
|
||||
proc exec(cmd: string) =
|
||||
if execShellCmd(cmd) != 0:
|
||||
quit "FAILURE: " & cmd
|
||||
|
||||
proc sameDirContents(expected, given: string) =
|
||||
for _, e in walkDir(expected):
|
||||
let g = given / splitPath(e).tail
|
||||
if fileExists(g):
|
||||
if readFile(e) != readFile(g):
|
||||
echo "FAILURE: files differ: ", e
|
||||
echo diffFiles(e, g).output
|
||||
inc failures
|
||||
else:
|
||||
echo "FAILURE: file does not exist: ", g
|
||||
inc failures
|
||||
|
||||
proc testWsConflict() =
|
||||
const myproject = "atlas/tests/ws_conflict/myproject"
|
||||
createDir(myproject)
|
||||
exec atlasExe & " --project=" & myproject & " --showGraph --genLock use https://github.com/apkg"
|
||||
sameDirContents("atlas/tests/ws_conflict/expected", myproject)
|
||||
removeDir("atlas/tests/ws_conflict/apkg")
|
||||
removeDir("atlas/tests/ws_conflict/bpkg")
|
||||
removeDir("atlas/tests/ws_conflict/cpkg")
|
||||
removeDir("atlas/tests/ws_conflict/dpkg")
|
||||
removeDir(myproject)
|
||||
|
||||
testWsConflict()
|
||||
if failures > 0: quit($failures & " failures occurred.")
|
||||
@@ -1,32 +0,0 @@
|
||||
version = "3.4.1"
|
||||
author = "disruptek"
|
||||
description = "a unittest framework with balls 🔴🟡🟢"
|
||||
license = "MIT"
|
||||
|
||||
# requires newTreeFrom
|
||||
requires "https://github.com/disruptek/grok >= 0.5.0 & < 1.0.0"
|
||||
requires "https://github.com/disruptek/ups < 1.0.0"
|
||||
requires "https://github.com/planetis-m/sync#810bd2d"
|
||||
#requires "https://github.com/c-blake/cligen < 2.0.0"
|
||||
|
||||
bin = @["balls"] # build the binary for basic test running
|
||||
installExt = @["nim"] # we need to install balls.nim also
|
||||
skipDirs = @["tests"] # so stupid... who doesn't want tests?
|
||||
#installFiles = @["balls.nim"] # https://github.com/nim-lang/Nim/issues/16661
|
||||
|
||||
task test, "run tests for ci":
|
||||
when defined(windows):
|
||||
exec "balls.cmd"
|
||||
else:
|
||||
exec "balls"
|
||||
|
||||
task demo, "produce a demo":
|
||||
exec "nim c --define:release balls.nim"
|
||||
when (NimMajor, NimMinor) != (1, 0):
|
||||
echo "due to nim bug #16307, use nim-1.0"
|
||||
quit 1
|
||||
exec """demo docs/demo.svg "nim c --out=\$1 examples/fails.nim""""
|
||||
exec """demo docs/clean.svg "nim c --define:danger -f --out=\$1 tests/test.nim""""
|
||||
exec "nim c --define:release --define:ballsDry balls.nim"
|
||||
exec """demo docs/runner.svg "balls""""
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
version = "0.0.4"
|
||||
author = "disruptek"
|
||||
description = "don't read too much into it"
|
||||
license = "MIT"
|
||||
requires "nim >= 1.0.0"
|
||||
@@ -1,7 +0,0 @@
|
||||
version = "0.2.2"
|
||||
author = "Juan Carlos"
|
||||
description = "Convert bytes to kilobytes, megabytes, gigabytes, etc."
|
||||
license = "MIT"
|
||||
srcDir = "src"
|
||||
|
||||
requires "nim >= 1.0.0" # https://github.com/juancarlospaco/nim-bytes2human/issues/2#issue-714338524
|
||||
@@ -1,10 +0,0 @@
|
||||
############# begin Atlas config section ##########
|
||||
--noNimblePath
|
||||
--path:"../balls"
|
||||
--path:"../grok"
|
||||
--path:"../ups"
|
||||
--path:"../sync"
|
||||
--path:"../npeg/src"
|
||||
--path:"../testes"
|
||||
--path:"../nim-bytes2human/src"
|
||||
############# end Atlas config section ##########
|
||||
@@ -1,48 +0,0 @@
|
||||
# Package
|
||||
|
||||
version = "0.24.1"
|
||||
author = "Ico Doornekamp"
|
||||
description = "a PEG library"
|
||||
license = "MIT"
|
||||
srcDir = "src"
|
||||
installExt = @["nim"]
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 0.19.0"
|
||||
|
||||
# Test
|
||||
|
||||
task test, "Runs the test suite":
|
||||
exec "nimble testc && nimble testcpp && nimble testarc && nimble testjs"
|
||||
|
||||
task testc, "C tests":
|
||||
exec "nim c -r tests/tests.nim"
|
||||
|
||||
task testcpp, "CPP tests":
|
||||
exec "nim cpp -r tests/tests.nim"
|
||||
|
||||
task testjs, "JS tests":
|
||||
exec "nim js -r tests/tests.nim"
|
||||
|
||||
task testdanger, "Runs the test suite in danger mode":
|
||||
exec "nim c -d:danger -r tests/tests.nim"
|
||||
|
||||
task testwin, "Mingw tests":
|
||||
exec "nim c -d:mingw tests/tests.nim && wine tests/tests.exe"
|
||||
|
||||
task test32, "32 bit tests":
|
||||
exec "nim c --cpu:i386 --passC:-m32 --passL:-m32 tests/tests.nim && tests/tests"
|
||||
|
||||
task testall, "Test all":
|
||||
exec "nimble test && nimble testcpp && nimble testdanger && nimble testjs && nimble testwin"
|
||||
|
||||
when (NimMajor, NimMinor) >= (1, 1):
|
||||
task testarc, "--gc:arc tests":
|
||||
exec "nim c --gc:arc -r tests/tests.nim"
|
||||
else:
|
||||
task testarc, "--gc:arc tests":
|
||||
exec "true"
|
||||
|
||||
task perf, "Test performance":
|
||||
exec "nim cpp -r -d:danger tests/performance.nim"
|
||||
@@ -1,36 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "bytes2human",
|
||||
"url": "https://github.com/juancarlospaco/nim-bytes2human",
|
||||
"method": "git",
|
||||
"tags": [
|
||||
"bytes",
|
||||
"human",
|
||||
"minimalism",
|
||||
"size"
|
||||
],
|
||||
"description": "Convert bytes to kilobytes, megabytes, gigabytes, etc.",
|
||||
"license": "LGPLv3",
|
||||
"web": "https://github.com/juancarlospaco/nim-bytes2human"
|
||||
},
|
||||
{
|
||||
"name": "npeg",
|
||||
"url": "https://github.com/zevv/npeg",
|
||||
"method": "git",
|
||||
"tags": [
|
||||
"PEG",
|
||||
"parser",
|
||||
"parsing",
|
||||
"regexp",
|
||||
"regular",
|
||||
"grammar",
|
||||
"lexer",
|
||||
"lexing",
|
||||
"pattern",
|
||||
"matching"
|
||||
],
|
||||
"description": "PEG (Parsing Expression Grammars) string matching library for Nim",
|
||||
"license": "MIT",
|
||||
"web": "https://github.com/zevv/npeg"
|
||||
}
|
||||
]
|
||||
@@ -1,10 +0,0 @@
|
||||
# Package
|
||||
|
||||
version = "1.4.0"
|
||||
author = "Antonis Geralis"
|
||||
description = "Useful synchronization primitives."
|
||||
license = "MIT"
|
||||
|
||||
# Deps
|
||||
|
||||
requires "nim >= 1.0.0"
|
||||
@@ -1,23 +0,0 @@
|
||||
version = "1.0.0"
|
||||
author = "disruptek"
|
||||
description = "a cure for salty testes"
|
||||
license = "MIT"
|
||||
|
||||
#requires "cligen >= 0.9.41 & <= 0.9.45"
|
||||
#requires "bump >= 1.8.18 & < 2.0.0"
|
||||
requires "https://github.com/disruptek/grok >= 0.0.4 & < 1.0.0"
|
||||
requires "https://github.com/juancarlospaco/nim-bytes2human"
|
||||
|
||||
bin = @["testes"] # build the binary for basic test running
|
||||
installExt = @["nim"] # we need to install testes.nim also
|
||||
skipDirs = @["tests"] # so stupid... who doesn't want tests?
|
||||
|
||||
task test, "run tests for ci":
|
||||
exec "nim c --run testes.nim"
|
||||
|
||||
task demo, "produce a demo":
|
||||
when (NimMajor, NimMinor) != (1, 0):
|
||||
echo "due to nim bug #16307, use nim-1.0"
|
||||
quit 1
|
||||
exec """demo docs/demo.svg "nim c --out=\$1 examples/balls.nim""""
|
||||
exec """demo docs/clean.svg "nim c --define:danger --out=\$1 tests/testicles.nim""""
|
||||
@@ -1,13 +0,0 @@
|
||||
version = "0.0.2"
|
||||
author = "disruptek"
|
||||
description = "a package handler"
|
||||
license = "MIT"
|
||||
|
||||
requires "npeg >= 0.23.2 & < 1.0.0"
|
||||
requires "https://github.com/disruptek/testes >= 1.0.0 & < 2.0.0"
|
||||
|
||||
task test, "run tests":
|
||||
when defined(windows):
|
||||
exec "testes.cmd"
|
||||
else:
|
||||
exec findExe"testes"
|
||||
@@ -1,2 +0,0 @@
|
||||
deps=""
|
||||
overrides="url.rules"
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"items": {
|
||||
"apkg": {
|
||||
"url": "file://./source/apkg",
|
||||
"commit": "#head"
|
||||
},
|
||||
"bpkg": {
|
||||
"url": "file://./source/bpkg",
|
||||
"commit": "1.0"
|
||||
},
|
||||
"cpkg": {
|
||||
"url": "file://./source/cpkg",
|
||||
"commit": "2.0"
|
||||
},
|
||||
"dpkg": {
|
||||
"url": "file://./source/dpkg",
|
||||
"commit": "1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
digraph deps {
|
||||
"file://./source/apkg/#head" [label=""];
|
||||
"file://./source/bpkg/1.0" [label=""];
|
||||
"file://./source/cpkg/1.0" [label="unused"];
|
||||
"file://./source/cpkg/2.0" [label=""];
|
||||
"file://./source/dpkg/1.0" [label=""];
|
||||
"file://./source/apkg/#head" -> "file://./source/bpkg/1.0";
|
||||
"file://./source/apkg/#head" -> "file://./source/cpkg/1.0";
|
||||
"file://./source/bpkg/1.0" -> "file://./source/cpkg/2.0";
|
||||
"file://./source/cpkg/2.0" -> "file://./source/dpkg/1.0";
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
requires "https://github.com/apkg"
|
||||
@@ -1,7 +0,0 @@
|
||||
############# begin Atlas config section ##########
|
||||
--noNimblePath
|
||||
--path:"../apkg"
|
||||
--path:"../bpkg"
|
||||
--path:"../cpkg"
|
||||
--path:"../dpkg"
|
||||
############# end Atlas config section ##########
|
||||
@@ -1,4 +0,0 @@
|
||||
# require first b and then c
|
||||
|
||||
requires "https://github.com/bpkg >= 1.0"
|
||||
requires "https://github.com/cpkg >= 1.0"
|
||||
@@ -1 +0,0 @@
|
||||
requires "https://github.com/cpkg >= 2.0"
|
||||
@@ -1 +0,0 @@
|
||||
# No dependency here!
|
||||
@@ -1 +0,0 @@
|
||||
requires "https://github.com/dpkg >= 1.0"
|
||||
@@ -1 +0,0 @@
|
||||
# empty for now
|
||||
@@ -1 +0,0 @@
|
||||
https://github.com/$+ -> file://./source/$#
|
||||
@@ -1,348 +0,0 @@
|
||||
#
|
||||
# Atlas Package Cloner
|
||||
# (c) Copyright 2021 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import std / [strutils, parseutils, algorithm]
|
||||
|
||||
type
|
||||
Version* = distinct string
|
||||
|
||||
VersionRelation* = enum
|
||||
verGe, # >= V -- Equal or later
|
||||
verGt, # > V
|
||||
verLe, # <= V -- Equal or earlier
|
||||
verLt, # < V
|
||||
verEq, # V
|
||||
verAny, # *
|
||||
verSpecial # #head
|
||||
|
||||
VersionReq* = object
|
||||
r: VersionRelation
|
||||
v: Version
|
||||
|
||||
VersionInterval* = object
|
||||
a: VersionReq
|
||||
b: VersionReq
|
||||
isInterval: bool
|
||||
|
||||
template versionKey*(i: VersionInterval): string = i.a.v.string
|
||||
|
||||
proc createQueryEq*(v: Version): VersionInterval =
|
||||
VersionInterval(a: VersionReq(r: verEq, v: v))
|
||||
|
||||
proc extractGeQuery*(i: VersionInterval): Version =
|
||||
if i.a.r in {verGe, verGt, verEq}:
|
||||
result = i.a.v
|
||||
else:
|
||||
result = Version""
|
||||
|
||||
proc `$`*(v: Version): string {.borrow.}
|
||||
|
||||
proc isSpecial(v: Version): bool =
|
||||
result = v.string.len > 0 and v.string[0] == '#'
|
||||
|
||||
proc isValidVersion*(v: string): bool {.inline.} =
|
||||
result = v.len > 0 and v[0] in {'#'} + Digits
|
||||
|
||||
proc isHead(v: Version): bool {.inline.} = cmpIgnoreCase(v.string, "#head") == 0
|
||||
|
||||
template next(l, p, s: untyped) =
|
||||
if l > 0:
|
||||
inc p, l
|
||||
if p < s.len and s[p] == '.':
|
||||
inc p
|
||||
else:
|
||||
p = s.len
|
||||
else:
|
||||
p = s.len
|
||||
|
||||
proc lt(a, b: string): bool {.inline.} =
|
||||
var i = 0
|
||||
var j = 0
|
||||
while i < a.len or j < b.len:
|
||||
var x = 0
|
||||
let l1 = parseSaturatedNatural(a, x, i)
|
||||
|
||||
var y = 0
|
||||
let l2 = parseSaturatedNatural(b, y, j)
|
||||
|
||||
if x < y:
|
||||
return true
|
||||
elif x == y:
|
||||
discard "continue"
|
||||
else:
|
||||
return false
|
||||
next l1, i, a
|
||||
next l2, j, b
|
||||
|
||||
result = false
|
||||
|
||||
proc `<`*(a, b: Version): bool =
|
||||
# Handling for special versions such as "#head" or "#branch".
|
||||
if a.isSpecial or b.isSpecial:
|
||||
if a.isHead: return false
|
||||
if b.isHead: return true
|
||||
# any order will do as long as the "sort" operation moves #thing
|
||||
# to the bottom:
|
||||
if a.isSpecial and b.isSpecial:
|
||||
return a.string < b.string
|
||||
return lt(a.string, b.string)
|
||||
|
||||
proc eq(a, b: string): bool {.inline.} =
|
||||
var i = 0
|
||||
var j = 0
|
||||
while i < a.len or j < b.len:
|
||||
var x = 0
|
||||
let l1 = parseSaturatedNatural(a, x, i)
|
||||
|
||||
var y = 0
|
||||
let l2 = parseSaturatedNatural(b, y, j)
|
||||
|
||||
if x == y:
|
||||
discard "continue"
|
||||
else:
|
||||
return false
|
||||
next l1, i, a
|
||||
next l2, j, b
|
||||
|
||||
result = true
|
||||
|
||||
proc `==`*(a, b: Version): bool =
|
||||
if a.isSpecial or b.isSpecial:
|
||||
result = a.string == b.string
|
||||
else:
|
||||
result = eq(a.string, b.string)
|
||||
|
||||
proc parseVer(s: string; start: var int): Version =
|
||||
if start < s.len and s[start] == '#':
|
||||
var i = start
|
||||
while i < s.len and s[i] notin Whitespace: inc i
|
||||
result = Version s.substr(start, i-1)
|
||||
start = i
|
||||
elif start < s.len and s[start] in Digits:
|
||||
var i = start
|
||||
while i < s.len and s[i] in Digits+{'.'}: inc i
|
||||
result = Version s.substr(start, i-1)
|
||||
start = i
|
||||
else:
|
||||
result = Version""
|
||||
|
||||
proc parseVersion*(s: string; start: int): Version =
|
||||
var i = start
|
||||
result = parseVer(s, i)
|
||||
|
||||
proc parseSuffix(s: string; start: int; result: var VersionInterval; err: var bool) =
|
||||
# >= 1.5 & <= 1.8
|
||||
# ^ we are here
|
||||
var i = start
|
||||
while i < s.len and s[i] in Whitespace: inc i
|
||||
# Nimble doesn't use the syntax `>= 1.5, < 1.6` but we do:
|
||||
if i < s.len and s[i] in {'&', ','}:
|
||||
inc i
|
||||
while i < s.len and s[i] in Whitespace: inc i
|
||||
if s[i] == '<':
|
||||
inc i
|
||||
var r = verLt
|
||||
if s[i] == '=':
|
||||
inc i
|
||||
r = verLe
|
||||
while i < s.len and s[i] in Whitespace: inc i
|
||||
result.b = VersionReq(r: r, v: parseVer(s, i))
|
||||
result.isInterval = true
|
||||
while i < s.len and s[i] in Whitespace: inc i
|
||||
# we must have parsed everything:
|
||||
if i < s.len:
|
||||
err = true
|
||||
|
||||
proc parseVersionInterval*(s: string; start: int; err: var bool): VersionInterval =
|
||||
var i = start
|
||||
while i < s.len and s[i] in Whitespace: inc i
|
||||
result = VersionInterval(a: VersionReq(r: verAny, v: Version""))
|
||||
if i < s.len:
|
||||
case s[i]
|
||||
of '*': result = VersionInterval(a: VersionReq(r: verAny, v: Version""))
|
||||
of '#', '0'..'9':
|
||||
result = VersionInterval(a: VersionReq(r: verEq, v: parseVer(s, i)))
|
||||
if result.a.v.isHead: result.a.r = verAny
|
||||
err = i < s.len
|
||||
of '=':
|
||||
inc i
|
||||
if i < s.len and s[i] == '=': inc i
|
||||
while i < s.len and s[i] in Whitespace: inc i
|
||||
result = VersionInterval(a: VersionReq(r: verEq, v: parseVer(s, i)))
|
||||
err = i < s.len
|
||||
of '<':
|
||||
inc i
|
||||
var r = verLt
|
||||
if i < s.len and s[i] == '=':
|
||||
r = verLe
|
||||
inc i
|
||||
while i < s.len and s[i] in Whitespace: inc i
|
||||
result = VersionInterval(a: VersionReq(r: r, v: parseVer(s, i)))
|
||||
parseSuffix(s, i, result, err)
|
||||
of '>':
|
||||
inc i
|
||||
var r = verGt
|
||||
if i < s.len and s[i] == '=':
|
||||
r = verGe
|
||||
inc i
|
||||
while i < s.len and s[i] in Whitespace: inc i
|
||||
result = VersionInterval(a: VersionReq(r: r, v: parseVer(s, i)))
|
||||
parseSuffix(s, i, result, err)
|
||||
else:
|
||||
err = true
|
||||
else:
|
||||
result = VersionInterval(a: VersionReq(r: verAny, v: Version"#head"))
|
||||
|
||||
proc parseTaggedVersions*(outp: string): seq[(string, Version)] =
|
||||
result = @[]
|
||||
for line in splitLines(outp):
|
||||
if not line.endsWith("^{}"):
|
||||
var i = 0
|
||||
while i < line.len and line[i] notin Whitespace: inc i
|
||||
let commitEnd = i
|
||||
while i < line.len and line[i] in Whitespace: inc i
|
||||
while i < line.len and line[i] notin Digits: inc i
|
||||
let v = parseVersion(line, i)
|
||||
if v != Version(""):
|
||||
result.add (line.substr(0, commitEnd-1), v)
|
||||
result.sort proc (a, b: (string, Version)): int =
|
||||
(if a[1] < b[1]: 1
|
||||
elif a[1] == b[1]: 0
|
||||
else: -1)
|
||||
|
||||
proc matches(pattern: VersionReq; v: Version): bool =
|
||||
case pattern.r
|
||||
of verGe:
|
||||
result = pattern.v < v or pattern.v == v
|
||||
of verGt:
|
||||
result = pattern.v < v
|
||||
of verLe:
|
||||
result = v < pattern.v or pattern.v == v
|
||||
of verLt:
|
||||
result = v < pattern.v
|
||||
of verEq, verSpecial:
|
||||
result = pattern.v == v
|
||||
of verAny:
|
||||
result = true
|
||||
|
||||
proc matches*(pattern: VersionInterval; v: Version): bool =
|
||||
if pattern.isInterval:
|
||||
result = matches(pattern.a, v) and matches(pattern.b, v)
|
||||
else:
|
||||
result = matches(pattern.a, v)
|
||||
|
||||
proc selectBestCommitMinVer*(data: openArray[(string, Version)]; elem: VersionInterval): string =
|
||||
for i in countdown(data.len-1, 0):
|
||||
if elem.matches(data[i][1]):
|
||||
return data[i][0]
|
||||
return ""
|
||||
|
||||
proc selectBestCommitMaxVer*(data: openArray[(string, Version)]; elem: VersionInterval): string =
|
||||
for i in countup(0, data.len-1):
|
||||
if elem.matches(data[i][1]): return data[i][0]
|
||||
return ""
|
||||
|
||||
proc toSemVer*(i: VersionInterval): VersionInterval =
|
||||
result = i
|
||||
if not result.isInterval and result.a.r in {verGe, verGt}:
|
||||
var major = 0
|
||||
let l1 = parseSaturatedNatural(result.a.v.string, major, 0)
|
||||
if l1 > 0:
|
||||
result.isInterval = true
|
||||
result.b = VersionReq(r: verLt, v: Version($(major+1)))
|
||||
|
||||
proc selectBestCommitSemVer*(data: openArray[(string, Version)]; elem: VersionInterval): string =
|
||||
result = selectBestCommitMaxVer(data, elem.toSemVer)
|
||||
|
||||
when isMainModule:
|
||||
template v(x): untyped = Version(x)
|
||||
|
||||
assert v"1.0" < v"1.0.1"
|
||||
assert v"1.0" < v"1.1"
|
||||
assert v"1.2.3" < v"1.2.4"
|
||||
assert v"2.0.0" < v"2.0.0.1"
|
||||
assert v"2.0.0" < v"20.0"
|
||||
assert not (v"1.10.0" < v"1.2.0")
|
||||
assert v"1.0" < v"#head"
|
||||
assert v"#branch" < v"#head"
|
||||
assert v"#branch" < v"1.0"
|
||||
assert not (v"#head" < v"#head")
|
||||
assert not (v"#head" < v"10.0")
|
||||
|
||||
const lines = """
|
||||
24870f48c40da2146ce12ff1e675e6e7b9748355 1.6.12
|
||||
b54236aaee2fc90200cb3a4e7070820ced9ce605 1.6.10
|
||||
f06dc8ee3baf8f64cce67a28a6e6e8a8cd9bf04b 1.6.8
|
||||
2f85924354af35278a801079b7ff3f8805ff1f5a 1.6.6
|
||||
007bf1cb52eac412bc88b3ca2283127ad578ec04 1.6.4
|
||||
ee18eda86eef2db0a49788bf0fc8e35996ba7f0d 1.6.2
|
||||
1a2a82e94269741b0d8ba012994dd85a53f36f2d 1.6.0
|
||||
074f7159752b0da5306bdedb3a4e0470af1f85c0 1.4.8
|
||||
4eb05ebab2b4d8b0cd00b19a72af35a2d767819a 1.4.6
|
||||
944c8e6d04a044611ed723391272f3c86781eadd 1.4.4
|
||||
cd090a6151b452b99d65c5173400d4685916f970 1.4.2
|
||||
01dd8c7a959adac4aa4d73abdf62cbc53ffed11b 1.4.0
|
||||
1420d508dc4a3e51137647926d4db2f3fa62f43c 1.2.18
|
||||
726e3bb1ffc6bacfaab0a0abf0209640acbac807 1.2.16
|
||||
80d2206e68cd74020f61e23065c7a22376af8de5 1.2.14
|
||||
ddfe3905964fe3db33d7798c6c6c4a493cbda6a3 1.2.12
|
||||
6d914b7e6edc29c3b8ab8c0e255bd3622bc58bba 1.2.10
|
||||
0d1a9f5933eab686ab3b527b36d0cebd3949a503 1.2.8
|
||||
a5a0a9e3cb14e79d572ba377b6116053fc621f6d 1.2.6
|
||||
f9829089b36806ac0129c421bf601cbb30c2842c 1.2.4
|
||||
8b03d39fd387f6a59c97c2acdec2518f0b18a230 1.2.2
|
||||
a8a4725850c443158f9cab38eae3e54a78a523fb 1.2.0
|
||||
8b5888e0545ee3d931b7dd45d15a1d8f3d6426ef 1.0.10
|
||||
7282e53cad6664d09e8c9efd0d7f263521eda238 1.0.8
|
||||
283a4137b6868f1c5bbf0dd9c36d850d086fa007 1.0.6
|
||||
e826ff9b48af376fdc65ba22f7aa1c56dc169b94 1.0.4
|
||||
4c33037ef9d01905130b22a37ddb13748e27bb7c 1.0.2
|
||||
0b6866c0dc48b5ba06a4ce57758932fbc71fe4c2 1.0.0
|
||||
a202715d182ce6c47e19b3202e0c4011bece65d8 0.20.2
|
||||
8ea451196bd8d77b3592b8b34e7a2c49eed784c9 0.20.0
|
||||
1b512cc259b262d06143c4b34d20ebe220d7fb5c 0.19.6
|
||||
be22a1f4e04b0fec14f7a668cbaf4e6d0be313cb 0.19.4
|
||||
5cbc7f6322de8460cc4d395ed0df6486ae68004e 0.19.2
|
||||
79934561e8dde609332239fbc8b410633e490c61 0.19.0
|
||||
9c53787087e36b1c38ffd670a077903640d988a8 0.18.0
|
||||
a713ffd346c376cc30f8cc13efaee7be1b8dfab9 0.17.2
|
||||
2084650f7bf6f0db6003920f085e1a86f1ea2d11 0.17.0
|
||||
f7f68de78e9f286b704304836ed8f20d65acc906 0.16.0
|
||||
48bd4d72c45f0f0202a0ab5ad9d851b05d363988 0.15.2
|
||||
dbee7d55bc107b2624ecb6acde7cabe4cb3f5de4 0.15.0
|
||||
0a4854a0b7bcef184f060632f756f83454e9f9de 0.14.2
|
||||
5333f2e4cb073f9102f30aacc7b894c279393318 0.14.0
|
||||
7e50c5b56d5b5b7b96e56b6c7ab5e104124ae81b 0.13.0
|
||||
49bce0ebe941aafe19314438fb724c081ae891aa 0.12.0
|
||||
70789ef9c8c4a0541ba24927f2d99e106a6fe6cc 0.11.2
|
||||
79cc0cc6e501c8984aeb5b217a274877ec5726bc 0.11.0
|
||||
46d829f65086b487c08d522b8d0d3ad36f9a2197 0.10.2
|
||||
9354d3de2e1ecc1747d6c42fbfa209fb824837c0 0.9.6
|
||||
6bf5b3d78c97ce4212e2dd4cf827d40800650c48 0.9.4
|
||||
220d35d9e19b0eae9e7cd1f1cac6e77e798dbc72 0.9.2
|
||||
7a70058005c6c76c92addd5fc21b9706717c75e3 0.9.0
|
||||
32b4192b3f0771af11e9d850046e5f3dd42a9a5f 0.8.14
|
||||
"""
|
||||
|
||||
proc p(s: string): VersionInterval =
|
||||
var err = false
|
||||
result = parseVersionInterval(s, 0, err)
|
||||
assert not err
|
||||
|
||||
let tags = parseTaggedVersions(lines)
|
||||
let query = p">= 1.2 & < 1.4"
|
||||
assert selectBestCommitMinVer(tags, query) == "a8a4725850c443158f9cab38eae3e54a78a523fb"
|
||||
|
||||
let query2 = p">= 1.2 & < 1.4"
|
||||
assert selectBestCommitMaxVer(tags, query2) == "1420d508dc4a3e51137647926d4db2f3fa62f43c"
|
||||
|
||||
let query3 = p">= 0.20.0"
|
||||
assert selectBestCommitSemVer(tags, query3) == "a202715d182ce6c47e19b3202e0c4011bece65d8"
|
||||
|
||||
let query4 = p"#head"
|
||||
assert selectBestCommitSemVer(tags, query4) == "24870f48c40da2146ce12ff1e675e6e7b9748355"
|
||||
@@ -66,7 +66,6 @@ Files: "doc"
|
||||
Files: "doc/html"
|
||||
Files: "tools"
|
||||
Files: "tools/debug/nim-gdb.py"
|
||||
Files: "atlas"
|
||||
Files: "nimpretty"
|
||||
Files: "testament"
|
||||
Files: "nimsuggest"
|
||||
|
||||
188
doc/atlas.md
188
doc/atlas.md
@@ -1,188 +0,0 @@
|
||||
# Atlas Package Cloner
|
||||
|
||||
Atlas is a simple package cloner tool. It manages an isolated workspace that
|
||||
contains projects and dependencies.
|
||||
|
||||
Atlas is compatible with Nimble in the sense that it supports the Nimble
|
||||
file format.
|
||||
|
||||
|
||||
## Concepts
|
||||
|
||||
Atlas uses three concepts:
|
||||
|
||||
1. Workspaces
|
||||
2. Projects
|
||||
3. Dependencies
|
||||
|
||||
### Workspaces
|
||||
|
||||
Every workspace is isolated, nothing is shared between workspaces.
|
||||
A workspace is a directory that has a file `atlas.workspace` inside it. Use `atlas init`
|
||||
to create a workspace out of the current working directory.
|
||||
|
||||
Projects plus their dependencies are stored in a workspace:
|
||||
|
||||
```
|
||||
$workspace / main project
|
||||
$workspace / other project
|
||||
$workspace / _deps / dependency A
|
||||
$workspace / _deps / dependency B
|
||||
```
|
||||
|
||||
The deps directory can be set via `--deps:DIR` during `atlas init`.
|
||||
|
||||
|
||||
### Projects
|
||||
|
||||
A workspace contains one or multiple "projects". These projects can use each other and it
|
||||
is easy to develop multiple projects at the same time.
|
||||
|
||||
### Dependencies
|
||||
|
||||
Inside a workspace there can be a `_deps` directory where your dependencies are kept. It is
|
||||
easy to move a dependency one level up and out the `_deps` directory, turning it into a project.
|
||||
Likewise, you can move a project to the `_deps` directory, turning it into a dependency.
|
||||
|
||||
The only distinction between a project and a dependency is its location. For dependency resolution
|
||||
a project always has a higher priority than a dependency.
|
||||
|
||||
|
||||
## No magic
|
||||
|
||||
Atlas works by managing two files for you, the `project.nimble` file and the `nim.cfg` file. You can
|
||||
edit these manually too, Atlas doesn't touch what should be left untouched.
|
||||
|
||||
|
||||
## How it works
|
||||
|
||||
Atlas uses git commits internally; version requirements are translated
|
||||
to git commits via `git show-ref --tags`.
|
||||
|
||||
Atlas uses URLs internally; Nimble package names are translated to URLs
|
||||
via Nimble's `packages.json` file.
|
||||
|
||||
Atlas does not call the Nim compiler for a build, instead it creates/patches
|
||||
a `nim.cfg` file for the compiler. For example:
|
||||
|
||||
```
|
||||
############# begin Atlas config section ##########
|
||||
--noNimblePath
|
||||
--path:"../nimx"
|
||||
--path:"../sdl2/src"
|
||||
--path:"../opengl/src"
|
||||
############# end Atlas config section ##########
|
||||
```
|
||||
|
||||
The version selection is deterministic, it picks up the *minimum* required
|
||||
version. Thanks to this design, lock files are much less important.
|
||||
|
||||
|
||||
|
||||
## Commands
|
||||
|
||||
Atlas supports the following commands:
|
||||
|
||||
|
||||
## Use <url> / <package name>
|
||||
|
||||
Clone the package behind `url` or `package name` and its dependencies into
|
||||
the `_deps` directory and make it available for your current project which
|
||||
should be in the current working directory. Atlas will create or patch
|
||||
the files `$project.nimble` and `nim.cfg` for you so that you can simply
|
||||
import the required modules.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
mkdir newproject
|
||||
cd newproject
|
||||
git init
|
||||
atlas use lexim
|
||||
# add `import lexim` to your example.nim file
|
||||
nim c example.nim
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Clone/Update <url>/<package name>
|
||||
|
||||
Clones a URL and all of its dependencies (recursively) into the workspace.
|
||||
Creates or patches a `nim.cfg` file with the required `--path` entries.
|
||||
|
||||
**Note**: Due to the used algorithms an `update` is the same as a `clone`.
|
||||
|
||||
|
||||
If a `<package name>` is given instead the name is first translated into an URL
|
||||
via `packages.json` or via a github search.
|
||||
|
||||
|
||||
### Search <term term2 term3 ...>
|
||||
|
||||
Search the package index `packages.json` for a package that the given terms
|
||||
in its description (or name or list of tags).
|
||||
|
||||
|
||||
### Install <proj.nimble>
|
||||
|
||||
Use the .nimble file to setup the project's dependencies.
|
||||
|
||||
### UpdateProjects / updateDeps [filter]
|
||||
|
||||
Update every project / dependency in the workspace that has a remote URL that
|
||||
matches `filter` if a filter is given. The project / dependency is only updated
|
||||
if there are no uncommitted changes.
|
||||
|
||||
### Others
|
||||
|
||||
Run `atlas --help` for more features.
|
||||
|
||||
|
||||
### Overrides
|
||||
|
||||
You can override how Atlas resolves a package name or a URL. The overrides use
|
||||
a simple pattern matching language and are flexible enough to integrate private
|
||||
gitlab repositories.
|
||||
|
||||
To setup an override file, edit the `$workspace/atlas.workspace` file to contain
|
||||
a line like `overrides="urls.rules"`. Then create a file `urls.rules` that can
|
||||
contain lines like:
|
||||
|
||||
```
|
||||
customProject -> https://gitlab.company.com/customProject
|
||||
https://github.com/araq/ormin -> https://github.com/useMyForkInstead/ormin
|
||||
```
|
||||
|
||||
The `$` has a special meaning in a pattern:
|
||||
|
||||
================= ========================================================
|
||||
``$$`` Matches a single dollar sign.
|
||||
``$*`` Matches until the token following the ``$*`` was found.
|
||||
The match is allowed to be of 0 length.
|
||||
``$+`` Matches until the token following the ``$+`` was found.
|
||||
The match must consist of at least one char.
|
||||
``$s`` Skips optional whitespace.
|
||||
================= ========================================================
|
||||
|
||||
For example, here is how to override any github link:
|
||||
|
||||
```
|
||||
https://github.com/$+ -> https://utopia.forall/$#
|
||||
```
|
||||
|
||||
You can use `$1` or `$#` to refer to captures.
|
||||
|
||||
|
||||
### Virtual Nim environments
|
||||
|
||||
Atlas supports setting up a virtual Nim environment via the `env` command. You can
|
||||
even install multiple different Nim versions into the same workspace.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
atlas env 1.6.12
|
||||
atlas env devel
|
||||
```
|
||||
|
||||
When completed, run `source nim-1.6.12/activate.sh` on UNIX and `nim-1.6.12/activate.bat` on Windows.
|
||||
26
koch.nim
26
koch.nim
@@ -11,6 +11,7 @@
|
||||
|
||||
const
|
||||
NimbleStableCommit = "168416290e49023894fc26106799d6f1fc964a2d" # master
|
||||
AtlasStableCommit = "#head"
|
||||
# examples of possible values: #head, #ea82b54, 1.2.3
|
||||
FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014"
|
||||
ChecksumsStableCommit = "b4c73320253f78e3a265aec6d9e8feb83f97c77b"
|
||||
@@ -72,6 +73,7 @@ Possible Commands:
|
||||
e.g. nimble)
|
||||
doesn't require network connectivity
|
||||
nimble builds the Nimble tool
|
||||
atlas builds the Atlas tool
|
||||
fusion installs fusion via Nimble
|
||||
|
||||
Boot options:
|
||||
@@ -156,6 +158,14 @@ proc bundleNimbleExe(latest: bool, args: string) =
|
||||
nimCompile("dist/nimble/src/nimble.nim",
|
||||
options = "-d:release --mm:refc --noNimblePath " & args)
|
||||
|
||||
proc bundleAtlasExe(latest: bool, args: string) =
|
||||
let commit = if latest: "HEAD" else: AtlasStableCommit
|
||||
cloneDependency(distDir, "https://github.com/nim-lang/atlas.git",
|
||||
commit = commit, allowBundled = true)
|
||||
# installer.ini expects it under $nim/bin
|
||||
nimCompile("dist/atlas/src/atlas.nim",
|
||||
options = "-d:release --noNimblePath " & args)
|
||||
|
||||
proc bundleNimsuggest(args: string) =
|
||||
nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim",
|
||||
options = "-d:danger " & args)
|
||||
@@ -179,7 +189,6 @@ proc bundleWinTools(args: string) =
|
||||
buildVccTool(args)
|
||||
nimCompile("tools/nimgrab.nim", options = "-d:ssl " & args)
|
||||
nimCompile("tools/nimgrep.nim", options = args)
|
||||
nimCompile("atlas/atlas.nim", options = args)
|
||||
nimCompile("testament/testament.nim", options = args)
|
||||
when false:
|
||||
# not yet a tool worth including
|
||||
@@ -193,6 +202,7 @@ proc bundleChecksums(latest: bool) =
|
||||
proc zip(latest: bool; args: string) =
|
||||
bundleChecksums(latest)
|
||||
bundleNimbleExe(latest, args)
|
||||
bundleAtlasExe(latest, args)
|
||||
bundleNimsuggest(args)
|
||||
bundleNimpretty(args)
|
||||
bundleWinTools(args)
|
||||
@@ -236,21 +246,17 @@ proc buildTools(args: string = "") =
|
||||
"--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints " & args,
|
||||
outputName = "nim_dbg")
|
||||
|
||||
nimCompileFold("Compile atlas", "atlas/atlas.nim", options = "-d:release " & args,
|
||||
outputName = "atlas")
|
||||
|
||||
proc testTools(args: string = "") =
|
||||
nimCompileFold("Compile nimgrep", "tools/nimgrep.nim",
|
||||
options = "-d:release " & args)
|
||||
when defined(windows): buildVccTool(args)
|
||||
bundleNimpretty(args)
|
||||
nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release " & args)
|
||||
nimCompileFold("Compile atlas", "atlas/atlas.nim", options = "-d:release " & args,
|
||||
outputName = "atlas")
|
||||
|
||||
proc nsis(latest: bool; args: string) =
|
||||
bundleChecksums(latest)
|
||||
bundleNimbleExe(latest, args)
|
||||
bundleAtlasExe(latest, args)
|
||||
bundleNimsuggest(args)
|
||||
bundleWinTools(args)
|
||||
# make sure we have generated the niminst executables:
|
||||
@@ -612,12 +618,6 @@ proc runCI(cmd: string) =
|
||||
execFold("build nimsuggest_testing", "nim c -o:bin/nimsuggest_testing -d:release nimsuggest/nimsuggest")
|
||||
execFold("Run nimsuggest tests", "nim r nimsuggest/tester")
|
||||
|
||||
execFold("Run atlas tests", "nim c -r -d:atlasTests atlas/atlas.nim clone https://github.com/disruptek/balls")
|
||||
# compile it again to get rid of `-d:atlasTests`:
|
||||
nimCompileFold("Compile atlas", "atlas/atlas.nim", options = "-d:release ",
|
||||
outputName = "atlas")
|
||||
execFold("Run more atlas tests", "nim c -r atlas/tester.nim")
|
||||
|
||||
kochExecFold("Testing booting in refc", "boot -d:release --mm:refc -d:nimStrictMode --lib:lib")
|
||||
|
||||
|
||||
@@ -731,6 +731,7 @@ when isMainModule:
|
||||
of "xtemp": xtemp(op.cmdLineRest)
|
||||
of "wintools": bundleWinTools(op.cmdLineRest)
|
||||
of "nimble": bundleNimbleExe(latest, op.cmdLineRest)
|
||||
of "atlas": bundleAtlasExe(latest, op.cmdLineRest)
|
||||
of "nimsuggest": bundleNimsuggest(op.cmdLineRest)
|
||||
# toolsNoNimble is kept for backward compatibility with build scripts
|
||||
of "toolsnonimble", "toolsnoexternal":
|
||||
@@ -738,6 +739,7 @@ when isMainModule:
|
||||
of "tools":
|
||||
buildTools(op.cmdLineRest)
|
||||
bundleNimbleExe(latest, op.cmdLineRest)
|
||||
bundleAtlasExe(latest, op.cmdLineRest)
|
||||
of "checksums":
|
||||
bundleChecksums(latest)
|
||||
of "pushcsource":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## (c) 2021 Andreas Rumpf
|
||||
|
||||
import std / strutils
|
||||
import ".." / compiler / [ast, idents, msgs, syntaxes, options, pathutils]
|
||||
import ".." / ".." / compiler / [ast, idents, msgs, syntaxes, options, pathutils]
|
||||
|
||||
type
|
||||
NimbleFileInfo* = object
|
||||
Reference in New Issue
Block a user