Skip to content

Commit

Permalink
WIP initial implementation done
Browse files Browse the repository at this point in the history
Compiling but no testing started yet
  • Loading branch information
viega committed Oct 7, 2023
1 parent 00ef8d4 commit dd8f5fa
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 132 deletions.
76 changes: 58 additions & 18 deletions files/con4m/components.nim
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import os, tables, types, nimutils, uri, lex
import os, tables, streams, strutils, httpclient, net, uri, options, nimutils,
types, lex, typecheck

# This has some cyclic dependencies, so we make sure C prototypes get
# generated with local scope only; we then do not import those modules.

proc parse(s: Stream, filename: string): Con4mNode {.importc, cdecl.}
proc parse(s: seq[Con4mToken], filename: string): Con4mNode {.importc, cdecl.}
proc checkTree(node: Con4mNode, s: ConfigState) {.importc, cdecl.}

var
defaultUrlStore = ""
componentInfo = Table[string, ComponentInfo]
programRoot: ComponentInfo
componentInfo: Table[string, ComponentInfo]
programRoot: ComponentInfo

proc fullComponentSpec*(name, location: string): string =
if location == "":
Expand All @@ -18,7 +19,7 @@ proc fullComponentSpec*(name, location: string): string =
else:
result = getAppDir().resolvePath()

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

else:
Expand All @@ -38,7 +39,7 @@ proc getComponentReference*(name: string, location: string): ComponentInfo =

proc fetchAttempt(url, extension: string): string =
var
finalExt = if extension.startswith("."): extension else "." & extension
finalExt = if extension.startsWith("."): extension else: "." & extension
uri = parseUri(url & finalExt)
context = newContext(verifyMode = CVerifyPeer)
client = newHttpClient(sslContext = context, timeout = 1000)
Expand All @@ -61,7 +62,7 @@ proc fetchComponent*(name, location: string, extension = ".c4m"):
result = getComponentReference(name, location)

if result.url.startsWith("https://"):
if checkForUpdates or result.hash == "":
if result.hash == "":
let
source = result.url.fetchAttempt(extension)
hash = sha256(source)
Expand Down Expand Up @@ -98,26 +99,26 @@ proc fetchComponent*(name, location: string, extension = ".c4m"):
(valid, toks) = stream.lex(result.url)

if not valid:
let msg = case tokens[^1].kind
let msg = case toks[^1].kind
of ErrorTok: "Invalid character found"
of ErrorLongComment: "Unterminated comment"
of ErrorStringLit: "Unterminated string"
of ErrorCharLit: "Invalid char literal"
of ErrorOtherLit: "Unterminated literal"
else: "Unknown error" # Not be possible w/o a lex bug

result.entrypoint = tokens.parse(result.url)
result.entrypoint = toks.parse(result.url)

proc loadComponent*(s: ConfigState, component: ComponentInfo) =
let savedComponent = s.currentComponent

if not result.typed:
state.currentComponent = result
state.currentComponent.entrypoint.checkTree(state)
state.currentComponent.typed = true
for component in state.currentComponent.componentsUsed:
s.loadComponent(component)
state.currentComponent = savedComponent
if not component.typed:
s.currentComponent = component
s.currentComponent.entrypoint.checkTree(s)
s.currentComponent.typed = true
for subcomponent in s.currentComponent.componentsUsed:
s.loadComponent(subcomponent)
s.currentComponent = savedComponent

# Now that any sub-components have loaded, we *could* do a check for
# cycles, but we are currently skipping it. It'll be runtime-only
Expand All @@ -126,9 +127,48 @@ proc loadComponent*(s: ConfigState, component: ComponentInfo) =
proc loadComponent*(s: ConfigState, name, location: string, extension = ".c4m"):
ComponentInfo =

if s.currentComponent == "":
if s.currentComponent == ComponentInfo(nil):
# Main file; register the component.
registerComponent(name, location)
programRoot = getComponentReference(name, location)

result = fetchComponent(name, location, extension)

s.loadComponent(result)

template setParamValue*(componentName, location, paramName: string,
value: Box, valueType: Con4mType,
paramStore: untyped) =

let component = getComponentReference(componentName, location)

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

let parameter = component.paramStore[paramName]

if valueType.unify(parameter.defaultType).isBottom():
raise newException(ValueError, "Incompatable type for: " & paramName)

parameter.value = some(value)

proc setVariableParamValue*(componentName, location, paramName: string,
value: Box, valueType: Con4mType) =
setParamValue(componentName, location, paramName, value, valueType,
varParams)

proc setAttributeParamValue*(componentName, location, paramName: string,
value: Box, valueType: Con4mType) =
setParamValue(componentName, location, paramName, value, valueType,
attrParams)

proc getAllVariableParamInfo*(name, location: string): seq[ParameterInfo] =
let component = getComponentReference(name, location)

for _, v in component.varParams:
result.add(v)

proc getAllAttrParamInfo*(name, location: string): seq[ParameterInfo] =
let component = getComponentReference(name, location)

for _, v in component.attrParams:
result.add(v)
9 changes: 5 additions & 4 deletions files/con4m/dollars.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ else:
result = tok.stream.readStr(tok.endPos - tok.startPos)
tok.stream.setPosition(pos)
template colorType(s: string): string =
stylize("<green>" & s & "</green>")
stylize("<green>" & s & "</green>").strip()

template colorLit(s: string): string =
stylize("<red>" & s & "</red>")
stylize("<red>" & s & "</red>").strip()

template colorNT(s: string): string =
stylize("<jazzberry>" & s & "</jazzberry>")
stylize("<jazzberry>" & s & "</jazzberry>").strip()

template colorT(s: string): string =
stylize("<orange>" & s & "</orange>")
stylize("<orange>" & s & "</orange>").strip()

type ReverseTVInfo = ref object
takenNames: seq[string]
Expand Down Expand Up @@ -174,6 +174,7 @@ template fmtT(name: string) =
proc `$`*(self: Con4mNode, i: int = 0): string =
case self.kind
of NodeBody: fmtNt("Body")
of NodeParamBody: fmtNt("ParamBody")
of NodeAttrAssign: fmtNt("AttrAssign")
of NodeAttrSetLock: fmtNt("AttrSetLock")
of NodeVarAssign: fmtNt("VarAssign")
Expand Down
6 changes: 3 additions & 3 deletions files/con4m/errmsg.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ proc formatCompilerError(msg: string,
let
me = getAppFileName().splitPath().tail

result = stylize("<red>" & me & "</red>" & curFileName & ": ")
result = stylize("<red>" & me & "</red>: " & curFileName & ": ").strip()

if t != nil:
result &= fmt"{t.lineNo}:{t.lineOffset+1}: "
Expand All @@ -64,12 +64,12 @@ proc formatCompilerError(msg: string,
pad = repeat(' ', offset + 1)

result &= "\n " & lines[line] & "\n"
result &= pad & "<strong>^</strong>".stylize()
result &= pad & "<bold>^</bold>".stylize().strip()

if verbosity in [c4vTrace, c4vMax]:
if tb != "":
result &= "\n"
result &= stylize("<bold>" & tb & "</bold>")
result &= stylize("<bold>" & tb & "</bold>").strip()
if ii.line != 0:
result &= "Exception thrown at: "
result &= ii.filename & "(" & $(ii.line) & ":" & $(ii.column) & ")\n"
Expand Down
88 changes: 48 additions & 40 deletions files/con4m/eval.nim
Original file line number Diff line number Diff line change
Expand Up @@ -200,35 +200,7 @@ template binaryOpWork(typeWeAreOping: typedesc,

node.value = pack(ret)

proc evalNode*(node: Con4mNode, s: ConfigState)

proc evalComponent*(s: ConfigState, module: string, url: string = "") =
let
savedGlobals = s.keptGlobals
savedScopes = s.frames
savedComponent = s.currentComponent
savedFuncOrigin = s.funcOrigin

s.currentComponent = getComponentReference(module, url)
s.funcOrigin = false
s.keptGlobals = s.currentComponent.savedVars

var newFrame = RuntimeFrame()
s.frames = @[newFrame]

for k, v in s.currentComponent.savedVars:
newFrame[k] = v.value

s.evalNode(s.currentComponent.entrypoint, s)

if len(s.frames) > 0:
for k, v in s.frames[0]:
s.currentComponent.savedVars[k] = v

s.funcOrigin = savedFuncOrigin
s.currentComponent = savedComponent
s.frames = savedScopes
s.keptGlobals = savedGlobals
proc evalComponent*(s: ConfigState, module: string, url: string = "")

proc evalNode*(node: Con4mNode, s: ConfigState) =
## This does the bulk of the work. Typically, it will descend into
Expand All @@ -238,18 +210,24 @@ proc evalNode*(node: Con4mNode, s: ConfigState) =
# These are explicit just to make sure I don't end up w/ implementation
# errors that baffle me.
of NodeFuncDef, NodeType, NodeVarDecl, NodeExportDecl, NodeVarSymNames,
NodeCallbackLit, NodeParameter:
NodeCallbackLit, NodeParameter, NodeParamBody:
return # Nothing to do, everything was done in the check phase.
of NodeUse:
let
kids = node.children
module = kids[0].getTokenText()
savedLock = s.lockAllAttrWrites

s.lockAllAttrWrites = true

if len(kids) == 1:
s.evalComponent(module)
else:
s.evalComponent(module, kids[1].getTokenText())

if not savedLock:
s.lockAllAttrWrites = false

of NodeReturn:
if node.children.len() != 0:
node.children[0].evalNode(s)
Expand Down Expand Up @@ -296,21 +274,18 @@ proc evalNode*(node: Con4mNode, s: ConfigState) =

case err.code
of errCantSet:
when defined(errOnOverride):
fatal(err.msg, node)
fatal(err.msg, node)
of errOk:
if node.kind == NodeAttrSetLock:
if node.kind == NodeAttrSetLock or s.lockAllAttrWrites:
node.attrRef.locked = true
else:
unreachable

of NodeVarAssign:
node.children[1].evalNode(s)

let name = node.children[0].getTokenText()

s.runtimeVarSet(name, node.children[1].value)

of NodeUnpack:
node.children[^1].evalNode(s)
let
Expand All @@ -321,7 +296,6 @@ proc evalNode*(node: Con4mNode, s: ConfigState) =
for i, item in tup:
let name = node.children[i].getTokenText()
s.runtimeVarSet(name, item)

of NodeIfStmt:
# This is the "top-level" node in an IF statement. The nodes
# below it will all be of kind NodeConditional NodeElse. We march
Expand Down Expand Up @@ -622,9 +596,43 @@ proc evalNode*(node: Con4mNode, s: ConfigState) =
else:
node.value = s.runtimeVarLookup(node.getTokenText())

proc evalComponent*(s: ConfigState, name, location: string,
extension = ".c4m") =

proc evalComponent*(s: ConfigState, module: string, url: string = "") =
let
component = s.loadComponent(name, location, extension)
savedGlobals = s.keptGlobals
savedScopes = s.frames
savedComponent = s.currentComponent
savedFuncOrigin = s.funcOrigin

s.currentComponent = getComponentReference(module, url)
s.funcOrigin = false
s.keptGlobals = s.currentComponent.savedVars

var newFrame = RuntimeFrame()
s.frames = @[newFrame]

for k, v in s.currentComponent.savedVars:
newFrame[k] = v.value

for k, v in s.currentComponent.varParams:
if v.value.isSome():
newFrame[k] = v.value
else:
raise newException(ValueError, "Component not configured")

for k, v in s.currentcomponent.attrParams:
if v.value.isSome():
s.attrs.attrSet(k, v.value.get(), v.defaultType)
else:
raise newException(ValueError, "Component not configured")

evalNode(s.currentComponent.entrypoint, s)

component.entrypoint.evalNode(s)
if len(s.frames) > 0:
for k, v in s.frames[0]:
s.currentComponent.savedVars[k].value = v

s.funcOrigin = savedFuncOrigin
s.currentComponent = savedComponent
s.frames = savedScopes
s.keptGlobals = savedGlobals
19 changes: 13 additions & 6 deletions files/con4m/parse.nim
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ type ParseCtx* = ref object
curTokIx*: int
nlWatch*: bool
nesting*: int
useTargets*:

var nodeId = 0
proc nnbase(k, t: auto, c: seq[Con4mNode], ti: Con4mType): Con4mNode =
Expand Down Expand Up @@ -139,7 +138,7 @@ proc isValidEndOfStatement(ctx: ParseCtx,
parseError("Expected either end of statement or one of: " &
tokens.join(", "))

template endOfStatement(ctx: ParseCtx) =
proc endOfStatement(ctx: ParseCtx) =
discard ctx.isValidEndOfStatement([])

# These productions need to be forward referenced.
Expand Down Expand Up @@ -849,8 +848,6 @@ proc section(ctx: ParseCtx): Con4mNode =
ctx.unconsume()

result.children.add(ctx.optionalBody())
if ctx.consume().kind != TtLBrace:
parseError("Expected '{' to start section")

proc useStmt(ctx: ParseCtx): Con4mNode =
let startTok = ctx.consume()
Expand Down Expand Up @@ -901,7 +898,17 @@ proc parameterBlock(ctx: ParseCtx): Con4mNode =
parseError("parameter keyword must be followed by an attribute " &
"(can be dotted) or `var` and a local variable name.")

result.children.add(ctx.optionalBody())
# Here we parse as body, but then swap out the top-level body node
# with a NodeParamBody node. The treecheck first pass will validate
# that the tree is the allowed subset of body for parameter blocks.
let
bodyParse = ctx.optionalBody()
paramBody = newNode(NodeParamBody, bodyParse.token.get())

for item in paramBody.children:
bodyParse.children.add(item)

result.children.add(bodyParse)

proc varAssign(ctx: ParseCtx): Con4mNode =
var
Expand Down Expand Up @@ -1084,7 +1091,7 @@ proc optionalBody(ctx: ParseCtx): Con4mNode =
# it, I'm sure I had a reason at the time)

ctx.nlWatch = false
if ctx.lookAhead().kind == TtLBrace:
if ctx.curTok().kind == TtLBrace:
discard ctx.consume()
result = ctx.body()
ctx.consumeOrError(TtRBrace)
Expand Down
Loading

0 comments on commit dd8f5fa

Please sign in to comment.