Skip to content

Commit

Permalink
Tweaks and bug fixes when doing the Chalk integration.
Browse files Browse the repository at this point in the history
  • Loading branch information
viega committed Oct 10, 2023
1 parent 4ecfc49 commit 1c32341
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 45 deletions.
41 changes: 28 additions & 13 deletions files/con4m/components.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ proc checkTree(node: Con4mNode, s: ConfigState) {.importc, cdecl.}
var defaultUrlStore = ""

proc fullComponentSpec*(name, location: string): string =
var path: string
if location == "":
if defaultUrlStore != "":
result = defaultUrlStore
path = defaultUrlStore
else:
result = getAppDir().resolvePath()
path = getAppDir().resolvePath()

elif location.startsWith("https://"):
result = location
path = location

else:
result = location.resolvePath()
path = location.resolvePath()

result = path.joinPath(name)

proc setDefaultStoreUrl*(url: string) =
once:
Expand All @@ -35,9 +38,10 @@ proc getComponentReference*(s: ConfigState, url: string): ComponentInfo =
proc getComponentReference*(s: ConfigState, name, loc: string): ComponentInfo =
return s.getComponentReference(fullComponentSpec(name, loc))

proc fetchAttempt(url, extension: string): string =
proc fetchAttempt(urlBase, extension: string): string =
var
finalExt = if extension.startsWith("."): extension else: "." & extension
url = if urlBase.endsWith("/"): urlBase[0 ..< ^1] else: urlBase
uri = parseUri(url & finalExt)
context = newContext(verifyMode = CVerifyPeer)
client = newHttpClient(sslContext = context, timeout = 1000)
Expand Down Expand Up @@ -73,21 +77,22 @@ proc cacheComponent*(component: ComponentInfo, stream: Stream) =
component.cacheComponent(stream.readAll())

proc fetchComponent*(item: ComponentInfo, extension = ".c4m", force = false) =
let fullPath = item.url & extension
var source: string

if force or item.hash == "":
if item.url.startsWith("https://"):
source = item.url.fetchAttempt(extension)
if fullPath.startsWith("https://"):
source = fullPath.fetchAttempt(extension)

if source == "":
raise newException(IOError, "Could not retrieve needed source " &
"file: " & item.url)
"file: " & fullPath)
else:
try:
source = item.url.readFile()
source = fullPath.readFile()
except:
raise newException(IOError, "Could not retrieve needed source " &
"file: " & item.url)
"file: " & fullPath)

item.cacheComponent(source, force)

Expand All @@ -102,14 +107,16 @@ proc fetchComponent*(s: ConfigState, name, loc: string, extension = ".c4m",

result = s.getComponentReference(name, loc)

fetchComponent(result, extension, force)
result.fetchComponent(extension, force)

proc getUsedComponents*(component: ComponentInfo, paramOnly = false):
seq[ComponentInfo] =
var
allDependents: seq[ComponentInfo] = @[component]

for sub in component.componentsUsed:
if sub notin result:
result.add(sub)
let sublist = sub.getUsedComponents()
for item in sublist:
if item == component:
Expand All @@ -123,7 +130,8 @@ proc getUsedComponents*(component: ComponentInfo, paramOnly = false):
else:
for item in allDependents:
if item.varParams.len() != 0 or item.attrParams.len() != 0:
result.add(item)
if item notin result:
result.add(item)

proc loadComponent*(s: ConfigState, component: ComponentInfo):
seq[ComponentInfo] {.discardable.} =
Expand Down Expand Up @@ -192,7 +200,12 @@ proc haveComponentFromUrl*(s: ConfigState, url: string): Option[ComponentInfo] =

let
(base, module, ext) = fullUrlToParts(url)
component = s.fetchComponent(module, base, ext)

if base.joinPath(module) notin s.components:
return none(ComponentInfo)

let
component = s.fetchComponent(module, base, ext, force = false)

if component.source != "":
return some(component)
Expand All @@ -206,6 +219,8 @@ template setParamValue*(s: ConfigState,
value: Box,
valueType: Con4mType,
paramStore: untyped) =
discard s.loadComponent(component)

if paramName notin component.paramStore:
raise newException(ValueError, "Parameter not found: " & paramName)

Expand Down
2 changes: 1 addition & 1 deletion files/con4m/dollars.nim
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ proc oneArgToString*(t: Con4mType,
b: Box,
outType = vTDefault,
lit = false): string =
case t.kind
case t.resolveTypeVars().kind
of TypeString:
if outType != vtNoLits and lit:
return "\"" & unpack[string](b) & "\""
Expand Down
36 changes: 32 additions & 4 deletions files/con4m/eval.nim
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,9 @@ proc evalNode*(node: Con4mNode, s: ConfigState) =
node.value = s.runtimeVarLookup(node.getTokenText())

proc evalComponent*(s: ConfigState, component: ComponentInfo) =
if s.programRoot == nil:
s.programRoot = component

let
savedScopes = s.frames
savedComponent = s.currentComponent
Expand Down Expand Up @@ -648,15 +651,40 @@ proc evalComponent*(s: ConfigState, component: ComponentInfo) =
raise newException(ValueError, "Component not configured")

for k, v in s.currentcomponent.attrParams:
if not s.attrs.attrExists(strutils.split(k, ".")):
# We'll first do a lookup of the scope the attr should be set in.
# If the scope needs to be created, vlSecDef will create it.
let
fqn = strutils.split(k, ".")
scopeAOrE = s.attrs.attrLookup(fqn[0 ..< ^1], 0, vlSecDef)

if scopeAOrE.isA(AttrErr):
let err = scopeAOrE.get(AttrErr)
raise newException(ValueError, err.msg)

let aOrS = scopeAOrE.get(AttrOrSub)

if aOrS.isA(Attribute):
let msg = strutils.join(fqn[0 ..< ^1], ".") &
" is an attribute, not a section, so cannot contain an attribute."
raise newException(ValueError, msg)

let scope = aOrS.get(AttrScope)

if fqn[^1] notin scope.contents:
if v.value.isSome():
s.attrs.attrSet(k, v.value.get(), v.defaultType)
scope.attrSet(fqn[^1], v.value.get(), v.defaultType)
elif v.default.isSome():
s.attrs.attrSet(k, v.default.get(), v.defaultType)
scope.attrSet(fqn[^1], v.default.get(), v.defaultType)
elif v.defaultCb.isSome():
s.attrs.attrSet(k, s.scall(v.defaultCb.get(), @[]).get(), v.defaultType)
scope.attrSet(fqn[^1], s.scall(v.defaultCb.get(), @[]).get(),
v.defaultType)
else:
raise newException(ValueError, "Component not configured")
else:
discard
# TODO: should validate that the item is an attribute and that
# the type matches, even though it should generally be validated
# elsewhere.

evalNode(s.currentComponent.entrypoint, s)

Expand Down
60 changes: 44 additions & 16 deletions files/con4m/params.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
import types, treecheck, options, tables, nimutils, eval, dollars
import types, treecheck, options, tables, nimutils, eval, dollars, strutils


proc validateParameter*(state: ConfigState, param: ParameterInfo):
Option[string] =
if param.value.isNone():
return some("<red>error:</red> Must provide a valid value.")

if param.validator.isSome():
let
boxOpt = state.sCall(param.validator.get(), @[param.value.get()])
err = unpack[string](boxOpt.get())

if err != "":
return some(err)

return none(string)


proc basicConfigureOneParam(state: ConfigState,
Expand All @@ -17,39 +33,51 @@ proc basicConfigureOneParam(state: ConfigState,

if boxOpt.isSome():
default = param.defaultType.oneArgToString(boxOpt.get())
default = " <b><i>" & default & "</i></b>"
default = default.stylize().strip()
let
short = param.shortDoc.getOrElse("No description provided")
long = param.doc.getOrElse("")
intro = "Configuring: <jazzberry>" & param.name & "</jazzberry> -- " &
"<i>" & short & "</i>\n" & long

print("### Configuring: " & param.name & " -- " & short & "\n" & long)

if boxOpt.isSome():
print("Press [enter] to accept default, or enter a value: ")
echo intro.stylize().strip()

while true:
if boxOpt.isSome():
echo("Default is: ", default)
echo("Press [enter] to accept default, or enter a value: ")
else:
echo("Please enter a value: ")

let line = stdin.readLine()

if line != "":
try:
boxOpt = some(line.parseConstLiteral(param.defaultType))
except:
print("<red>error:</red> " & getCurrentExceptionMsg())

if boxOpt.isNone():
print("<red>error:</red> Must enter a valid value.")
continue
elif line == "":
print("<atomiclime>success:</atomiclime> Accepting the default value.")
else:
print("<atomiclime>success:</atomiclime> Value accepted.")
continue

param.value = boxOpt
break

let err = state.validateParameter(param)

if err.isNone():
break

print(err.get())

proc basicConfigureParameters*(state: ConfigState,
component: ComponentInfo,
componentList: seq[ComponentInfo]) =
print("# Congiguring Component: " & component.url)
print("# Configuring Component: " & component.url)
for subcomp in componentList:
for _, param in subcomp.varParams:
for name, param in subcomp.varParams:
state.basicConfigureOneParam(subcomp, param)

for name, param in subcomp.attrParams:
state.basicConfigureOneParam(subcomp, param)
print("# Finished configuration " & component.url)
echo("Press [enter] to continue.")
discard stdin.readLine()
2 changes: 1 addition & 1 deletion files/con4m/parse.nim
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ proc useStmt(ctx: ParseCtx): Con4mNode =
discard ctx.consume()

if ctx.curTok().kind == TtStringLit:
result.children.add(newNode(NodeSimpLit, tok))
result.children.add(newNode(NodeSimpLit, ctx.consume()))
else:
parseError("Argument to 'from' must be a string literal consisting " &
"of either a https URL, or a local file path to a directory with the " &
Expand Down
1 change: 1 addition & 0 deletions files/con4m/st.nim
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ proc attrLookup*(scope: AttrScope,

return newScope.attrLookup(parts, ix + 1, op)


proc attrExists*(scope: AttrScope, parts: openarray[string]): bool =
return scope.attrLookup(parts, 0, vlExists).isA(AttrOrSub)

Expand Down
22 changes: 12 additions & 10 deletions files/con4m/treecheck.nim
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ proc nodeToIntLit(node: Con4mNode): Box =

proc nodeToTrueLit(node: Con4mNode): Box =
node.typeInfo = boolType
return pack(true)
result = pack(true)

proc nodeToFalseLit(node: Con4mNode): Box =
node.typeInfo = boolType
return pack(false)
result = pack(false)

proc nodeToFloatLit(node: Con4mNode): Box =
node.typeInfo = floatType
Expand Down Expand Up @@ -551,8 +551,8 @@ proc checkNode(node: Con4mNode, s: ConfigState) =
node.children[2].getType().unify(intType).isBottom():
fatal("For index ranges must be integers.")
of NodeSimpLit:
if s.secondPass:
return
#if s.secondPass:
# return
node.value = node.nodeToSimpLit()
of NodeUnary:
node.checkKids(s)
Expand Down Expand Up @@ -1079,8 +1079,12 @@ proc checkNode(node: Con4mNode, s: ConfigState) =
name = node.children[0].children[0].getTokenText()
else:
attr = true
var parts: seq[string]

for item in node.children[0].children:
name &= item.getTokenText()
parts.add item.getTokenText()

name = parts.join(".")

if not s.secondPass:
# Force the second pass. We'll validate that the types are
Expand Down Expand Up @@ -1132,7 +1136,7 @@ proc checkNode(node: Con4mNode, s: ConfigState) =
else:
unreachable
if attr:
s.currentComponent.attrParams[name] = paramObj
s.currentComponent.attrParams[name] = paramObj
else:
let sym = node.addVariable(name)

Expand Down Expand Up @@ -1269,10 +1273,8 @@ proc parseConstLiteral*(s: string, t: Con4mType): Box =
try:
let tree = s.parseLiteral()

if tree.isConstLit():
tree.checkNode(ConfigState())

if not tree.typeInfo.unify(t).isBottom():
tree.checkNode(ConfigState())
if tree.isConstLit() and tree.typeInfo.unify(t).isBottom():
return tree.value
except:
discard
Expand Down

0 comments on commit 1c32341

Please sign in to comment.