diff --git a/src/tim/engine/compilers/html.nim b/src/tim/engine/compilers/html.nim
index d70ebe4..830ba67 100755
--- a/src/tim/engine/compilers/html.nim
+++ b/src/tim/engine/compilers/html.nim
@@ -88,6 +88,7 @@ const
domSetAttribute = "$1.setAttribute('$2','$3');"
domInsertAdjacentElement = "$1.insertAdjacentElement('beforeend',$2);"
domInnerText = "$1.innerText=\"$2\";"
+ stdlibPaths = ["std/system", "std/strings", "std/arrays", "std/os"]
when not defined timStandalone:
# Scope API, available for library version of TimEngine
@@ -109,12 +110,12 @@ when not defined timStandalone:
else:
c.globalScope[node.varName] = node
of ntFunction:
- if c.ast.src != "std/system":
+ if c.ast.src notin stdlibPaths:
if scopetables.len > 0:
scopetables[^1][node.fnIdent] = node
- else:
- c.globalScope[node.fnIdent] = node
- else: discard # todo
+ else: c.globalScope[node.fnIdent] = node
+ else:
+ c.globalScope[node.fnIdent] = node
else: discard
proc getCurrentScope(c: var HtmlCompiler,
@@ -380,6 +381,34 @@ proc walkAccessorStorage(c: var HtmlCompiler,
let lhs = c.bracketEvaluator(lhs, scopetables)
if likely(lhs != nil):
return c.walkAccessorStorage(lhs, rhs, scopetables)
+ of ntLitString:
+ case rhs.nt
+ of ntLitInt:
+ try:
+ return ast.newString($(lhs.sVal[rhs.iVal]))
+ except Defect:
+ compileErrorWithArgs(indexDefect, lhs.meta,
+ [$(rhs.iVal), "0.." & $(lhs.arrayItems.high)])
+ of ntIndexRange:
+ let
+ l = c.getValue(rhs.rangeNodes[0], scopetables)
+ r = c.getValue(rhs.rangeNodes[1], scopetables)
+ if likely(l != nil and r != nil):
+ let l = l.iVal
+ let r = r.iVal
+ try:
+ case rhs.rangeLastIndex
+ of false:
+ result = ast.newString($(lhs.sVal[l..r]))
+ of true:
+ result = ast.newString($(lhs.sVal[l..^r]))
+ except Defect:
+ let someRange =
+ if rhs.rangeLastIndex: $(l) & "..^" & $(r)
+ else: $(l) & ".." & $(r)
+ compileErrorWithArgs(indexDefect, lhs.meta,
+ [someRange, "0.." & $(lhs.sVal.high)])
+ else: discard
of ntLitArray:
case rhs.nt
of ntLitInt:
@@ -409,7 +438,19 @@ proc walkAccessorStorage(c: var HtmlCompiler,
compileErrorWithArgs(indexDefect, lhs.meta,
[someRange, "0.." & $(lhs.arrayItems.high)])
else: discard # todo error?
- else: compileErrorWithArgs(invalidAccessorStorage,
+ else:
+ case rhs.nt
+ of ntIdent:
+ let some = c.getScope(rhs.identName, scopetables)
+ if likely(some.scopeTable != nil):
+ case some.scopeTable[rhs.identName].nt
+ of ntFunction:
+ # evaluate a function call and return the result
+ # if the retun type is not void, otherwise nil
+ return c.unsafeCall(lhs, some.scopeTable[rhs.identName], scopetables)
+ else: discard
+ else: discard
+ compileErrorWithArgs(invalidAccessorStorage,
rhs.meta, [rhs.toString, $lhs.nt])
else: discard
@@ -470,6 +511,9 @@ proc infixEvaluator(c: var HtmlCompiler, lhs, rhs: Node,
infixOp: InfixOp, scopetables: var seq[ScopeTable]): bool =
# Evaluates comparison expressions
if unlikely(lhs == nil or rhs == nil): return
+ let lhs = c.getValue(lhs, scopetables)
+ let rhs = c.getValue(rhs, scopetables)
+ if unlikely(lhs == nil or rhs == nil): return
case infixOp:
of EQ:
case lhs.nt:
@@ -662,7 +706,6 @@ proc getValue(c: var HtmlCompiler, node: Node,
# evaluates an identifier
let some = c.getScope(node.identName, scopetables)
if likely(some.scopeTable != nil):
- # echo some.scopeTable[node.identName].nt
case some.scopeTable[node.identName].nt
of ntFunction:
# evaluate a function call and return the result
@@ -675,6 +718,8 @@ proc getValue(c: var HtmlCompiler, node: Node,
return c.data["local"].toTimNode
if node.identName == "app":
return c.data["global"].toTimNode
+ if node.identArgs.len > 0:
+ compileErrorWithArgs(fnUndeclared, [node.identName])
compileErrorWithArgs(undeclaredVariable, [node.identName])
of ntAssignableSet, ntIndexRange:
# return literal nodes
@@ -1380,10 +1425,12 @@ proc walkNodes(c: var HtmlCompiler, nodes: seq[Node],
of ntIdent:
# case parentNodeType
# of ntHtmlElement:
- # let returnNode = c.fnCall(node, scopetables)
- # if likely(returnNode != nil):
- # write returnNode, true, false
+ # echo node
+ # let returnNode = c.fnCall(node, scopetables)
+ # if likely(returnNode != nil):
+ # write returnNode, true, false
# else:
+ # discard
# let x = c.fnCall(node, scopetables)
# if unlikely x != nil:
# if parentNodeType != ntFunction and x.nt != ntHtmlElement:
diff --git a/src/tim/engine/parser.nim b/src/tim/engine/parser.nim
index 455034d..7a1691b 100755
--- a/src/tim/engine/parser.nim
+++ b/src/tim/engine/parser.nim
@@ -89,6 +89,9 @@ proc parseMathExp(p: var Parser, lhs: Node): Node {.gcsafe.}
proc parseCompExp(p: var Parser, lhs: Node): Node {.gcsafe.}
proc parseTernaryExpr(p: var Parser, lhs: Node): Node {.gcsafe.}
+proc parseModule(engine: TimEngine, moduleName: string,
+ code: SourceCode = SourceCode("")): Ast {.gcsafe.}
+
template caseNotNil(x: Node, body): untyped =
if likely(x != nil):
body
@@ -286,7 +289,9 @@ proc parseDotExpr(p: var Parser, lhs: Node): Node {.gcsafe.} =
result.lhs = lhs
walk p # tkDot
if p.isFnCall():
- result.rhs = p.pFunctionCall()
+ let fnCallNode = p.pFunctionCall()
+ caseNotNil fnCallNode:
+ result.rhs = fnCallNode
elif p.curr is tkIdentifier:
result.rhs = ast.newIdent(p.curr)
walk p
@@ -841,12 +846,16 @@ prefixHandle pInclude:
prefixHandle pImport:
# parse `@import`
- if likely p.next is tkString:
- let tk = p.curr
- walk p
- result = ast.newNode(ntImport, tk)
- add result.modules, p.curr.value
- walk p
+ {.gcsafe.}:
+ if likely p.next is tkString:
+ let tk = p.curr
+ walk p
+ result = ast.newNode(ntImport, tk)
+ add result.modules, p.curr.value
+ p.tree.modules[p.curr.value] =
+ p.engine.parseModule(p.curr.value, std(p.curr.value)[1])
+ p.tree.modules[p.curr.value].src = p.curr.value
+ walk p
prefixHandle pSnippet:
case p.curr.kind
@@ -973,6 +982,10 @@ prefixHandle pFunctionCall:
walk p, 2 # we know tkLP is next so we'll skip it
while p.curr isnot tkRP:
let argNode = p.getPrefixOrInfix(includes = tkAssignableSet)
+ if p.curr is tkComma and p.next in tkAssignableSet:
+ walk p
+ elif p.curr isnot tkRP:
+ return nil
caseNotNil argNode:
add result.identArgs, argNode
walk p # tkRP
@@ -1212,27 +1225,28 @@ template collectImporterErrors =
p.handle.hasErrors = true
proc parseModule(engine: TimEngine, moduleName: string,
- code: SourceCode = SourceCode("")): Ast =
- var p = Parser(
- tree: Ast(src: moduleName),
- engine: engine,
- lex: newLexer(code.string, allowMultilineStrings = true),
- logger: Logger(filePath: "")
- )
- p.curr = p.lex.getToken()
- p.next = p.lex.getToken()
- # p.skipComments() # if any
- while p.curr isnot tkEOF:
- if unlikely(p.lex.hasError):
- p.logger.newError(internalError, p.curr.line,
- p.curr.col, false, p.lex.getError)
- if unlikely(p.hasErrors):
- break
- let node = p.parseRoot()
- if node != nil:
- add p.tree.nodes, node
- p.lex.close()
- result = p.tree
+ code: SourceCode = SourceCode("")): Ast {.gcsafe.} =
+ {.gcsafe.}:
+ var p = Parser(
+ tree: Ast(src: moduleName),
+ engine: engine,
+ lex: newLexer(code.string, allowMultilineStrings = true),
+ logger: Logger(filePath: "")
+ )
+ p.curr = p.lex.getToken()
+ p.next = p.lex.getToken()
+ # p.skipComments() # if any
+ while p.curr isnot tkEOF:
+ if unlikely(p.lex.hasError):
+ p.logger.newError(internalError, p.curr.line,
+ p.curr.col, false, p.lex.getError)
+ if unlikely(p.hasErrors):
+ break
+ let node = p.parseRoot()
+ if node != nil:
+ add p.tree.nodes, node
+ p.lex.close()
+ result = p.tree
proc initSystemModule(p: var Parser) =
## Make `std/system` available by default
diff --git a/src/tim/engine/stdlib.nim b/src/tim/engine/stdlib.nim
new file mode 100644
index 0000000..fa95c6c
--- /dev/null
+++ b/src/tim/engine/stdlib.nim
@@ -0,0 +1,405 @@
+# A super fast stylesheet language for cool kids
+#
+# (c) 2023 George Lemon | LGPL License
+# Made by Humans from OpenPeeps
+# https://github.com/openpeeps/bro
+
+import std/[macros, enumutils, critbits]
+import ./ast
+
+# std lib dependencies
+import pkg/[jsony, nyml]
+import std/[os, math, fenv, strutils, sequtils,
+ random, unicode, json, base64]
+
+# import ./css
+
+type
+ Arg* = tuple[name: string, value: Node]
+ NimCall* = proc(args: openarray[Arg], returnType: NodeType = ntUnknown): Node
+
+ Module = CritBitTree[NimCall]
+ SourceCode* = distinct string
+
+ Stdlib* = CritBitTree[(Module, SourceCode)]
+
+ StringsModule* = object of CatchableError
+ ArraysModule* = object of CatchableError
+ OSModule* = object of CatchableError
+ ColorsModule* = object of CatchableError
+ SystemModule* = object of CatchableError
+
+var
+ stdlib*: Stdlib
+ strutilsModule {.threadvar.},
+ sequtilsModule {.threadvar.},
+ osModule {.threadvar.},
+ critbitsModule {.threadvar.},
+ systemModule {.threadvar.},
+ mathModule {.threadvar.},
+ chromaModule {.threadvar.}: Module
+
+proc toNimSeq*(node: Node): seq[string] =
+ for item in node.arrayItems:
+ result.add(item.sVal)
+
+macro initStandardLibrary() =
+ type
+ Wrapper = proc(args: seq[Node]): Node {.nimcall.}
+
+ FwdType = enum
+ fwdProc
+ fwdIterator
+
+ Forward = object
+ fwdType: FwdType
+ id: string
+ # function identifier (nim side)
+ alias: string
+ # if not provided, it will use the `id`
+ # for the bass function name
+ returns: NodeType
+ # the return type, one of: `ntLitString`, `ntLitInt`,
+ # `ntLitBool`, `ntLitFloat`, `ntLitArray`, `ntLitObject`
+ args: seq[(NodeType, string)]
+ # a seq of NodeType for type matching
+ wrapper: NimNode
+ # wraps nim function
+ hasWrapper: bool
+ loadFrom: string
+
+ proc addFunction(id: string, args: openarray[(NodeType, string)], nt: NodeType): string =
+ var p = args.map do:
+ proc(x: (NodeType, string)): string =
+ "$1: $2" % [x[1], $(x[0])]
+ result = "fn $1*($2): $3\n" % [id, p.join(", "), $nt]
+
+ proc fwd(id: string, returns: NodeType, args: openarray[(NodeType, string)] = [],
+ alias = "", wrapper: NimNode = nil, loadFrom = ""): Forward =
+ Forward(id: id, returns: returns, args: args.toSeq,
+ alias: alias, wrapper: wrapper, hasWrapper: wrapper != nil,
+ loadFrom: loadFrom)
+
+ # proc `*`(nt: NodeType, count: int): seq[NodeType] =
+ # for i in countup(1, count):
+ # result.add(nt)
+
+ proc argToSeq[T](arg: Arg): T =
+ toNimSeq(arg.value)
+
+ template formatWrapper: untyped =
+ try:
+ ast.newString(format(args[0].value.sVal, argToSeq[seq[string]](args[1])))
+ except ValueError as e:
+ raise newException(StringsModule, e.msg)
+
+ template systemStreamFunction: untyped =
+ try:
+ let filepath =
+ if not isAbsolute(args[0].value.sVal):
+ absolutePath(args[0].value.sVal)
+ else: args[0].value.sVal
+ let str = readFile(filepath)
+ let ext = filepath.splitFile.ext
+ if ext == ".json":
+ return ast.newStream(str.fromJson(JsonNode))
+ elif ext in [".yml", ".yaml"]:
+ return ast.newStream(yaml(str).toJson.get)
+ else:
+ echo "error"
+ except IOError as e:
+ raise newException(SystemModule, e.msg)
+ except JsonParsingError as e:
+ raise newException(SystemModule, e.msg)
+
+ template systemRandomize: untyped =
+ randomize()
+ ast.newInteger(rand(args[0].value.iVal))
+
+ template systemInc: untyped =
+ inc args[0].value.iVal
+ echo args[0].value
+ args[0].value
+
+ template convertToString: untyped =
+ var str: ast.Node
+ var val = args[0].value
+ case val.nt:
+ of ntLitInt:
+ str = ast.newString($(val.iVal))
+ of ntLitFloat:
+ str = ast.newString($(val.fVal))
+ else: discard
+ str
+
+ let
+ fnSystem = @[
+ # fwd("json", ntStream, [(ntLitString, "path")], wrapper = getAst(systemStreamFunction())),
+ # fwd("yaml", ntStream, [(ntLitString, "path")], wrapper = getAst(systemStreamFunction())),
+ fwd("rand", ntLitInt, [(ntLitInt, "max")], "random", wrapper = getAst(systemRandomize())),
+ fwd("len", ntLitInt, [(ntLitString, "x")]),
+ # fwd("len", ntLitInt, [(ntLitArray, "x")]),
+ fwd("encode", ntLitString, [(ntLitString, "x")], loadFrom = "base64"),
+ fwd("decode", ntLitString, [(ntLitString, "x")], loadFrom = "base64"),
+ fwd("toString", ntLitString, [(ntLitInt, "x")], wrapper = getAst(convertToString()))
+ # fwd("int", ntInt, [(ntLitInt, "x")], "increment", wrapper = getAst(systemInc())),
+ ]
+
+ let
+ fnMath = @[
+ fwd("ceil", ntLitFloat, [(ntLitFloat, "x")]),
+ # fwd("clamp") need to add support for ranges
+ fwd("floor", ntLitFloat, [(ntLitFloat, "x")]),
+ fwd("max", ntLitInt, [(ntLitInt, "x"), (ntLitInt, "y")], loadFrom = "system"),
+ fwd("min", ntLitInt, [(ntLitInt, "x"), (ntLitInt, "y")], loadFrom = "system"),
+ fwd("round", ntLitFloat, [(ntLitFloat, "x")]),
+ # fwd("abs", ntLitInt, [(ntLitInt, "x")]),
+ fwd("hypot", ntLitFloat, [(ntLitFloat, "x"), (ntLitFloat, "y")]),
+ fwd("log", ntLitFloat, [(ntLitFloat, "x"), (ntLitFloat, "base")]),
+ fwd("pow", ntLitFloat, [(ntLitFloat, "x"), (ntLitFloat, "y")]),
+ fwd("sqrt", ntLitFloat, [(ntLitFloat, "x")]),
+ fwd("cos", ntLitFloat, [(ntLitFloat, "x")]),
+ fwd("sin", ntLitFloat, [(ntLitFloat, "x")]),
+ fwd("tan", ntLitFloat, [(ntLitFloat, "x")]),
+ fwd("arccos", ntLitFloat, [(ntLitFloat, "x")], "acos"),
+ fwd("arcsin", ntLitFloat, [(ntLitFloat, "x")], "asin"),
+ fwd("radToDeg", ntLitFloat, [(ntLitFloat, "d")], "rad2deg"),
+ fwd("degToRad", ntLitFloat, [(ntLitFloat, "d")], "deg2rad"),
+ fwd("arctan", ntLitFloat, [(ntLitFloat, "x")], "atan"),
+ fwd("arctan2", ntLitFloat, [(ntLitFloat, "x"), (ntLitFloat, "y")], "atan2"),
+ fwd("trunc", ntLitFloat, [(ntLitFloat, "x")]),
+ ]
+ # std/strings
+ # implements common functions for working with strings
+ # https://nim-lang.github.io/Nim/strutils.html
+ let
+ fnStrings = @[
+ fwd("endsWith", ntLitBool, [(ntLitString, "s"), (ntLitString, "suffix")]),
+ fwd("startsWith", ntLitBool, [(ntLitString, "s"), (ntLitString, "prefix")]),
+ fwd("capitalizeAscii", ntLitString, [(ntLitString, "s")], "capitalize"),
+ fwd("replace", ntLitString, [(ntLitString, "s"), (ntLitString, "sub"), (ntLitString, "by")]),
+ fwd("toLowerAscii", ntLitString, [(ntLitString, "s")], "toLower"),
+ fwd("contains", ntLitBool, [(ntLitString, "s"), (ntLitString, "sub")]),
+ fwd("parseBool", ntLitBool, [(ntLitString, "s")]),
+ fwd("parseInt", ntLitInt, [(ntLitString, "s")]),
+ fwd("parseFloat", ntLitFloat, [(ntLitString, "s")], "toFloat"),
+ fwd("format", ntLitString, [(ntLitString, "s"), (ntLitArray, "a")], wrapper = getAst(formatWrapper()))
+ ]
+
+ # std/arrays
+ # implements common functions for working with arrays (sequences)
+ # https://nim-lang.github.io/Nim/sequtils.html
+
+ template arraysContains: untyped =
+ ast.newBool(system.contains(toNimSeq(args[0].value), args[1].value.sVal))
+
+ template arraysAdd: untyped =
+ add(args[0].value.arrayItems, args[1].value)
+
+ template arraysShift: untyped =
+ try:
+ delete(args[0].value.arrayItems, 0)
+ except IndexDefect as e:
+ raise newException(ArraysModule, e.msg)
+
+ template arraysPop: untyped =
+ try:
+ delete(args[0].value.arrayItems, args[0].value.arrayItems.high)
+ except IndexDefect as e:
+ raise newException(ArraysModule, e.msg)
+
+ template arraysShuffle: untyped =
+ randomize()
+ shuffle(args[0].value.arrayItems)
+
+ template arraysJoin: untyped =
+ ast.newString(strutils.join(toNimSeq(args[0].value), args[1].value.sVal))
+
+ template arraysDelete: untyped =
+ delete(args[0].value.arrayItems, args[1].value.iVal)
+
+ template arraysFind: untyped =
+ for i in 0..args[0].value.arrayItems.high:
+ if args[0].value.arrayItems[i].sVal == args[1].value.sVal:
+ return ast.newInteger(i)
+
+ let
+ fnArrays = @[
+ fwd("contains", ntLitBool, [(ntLitArray, "x"), (ntLitString, "item")], wrapper = getAst arraysContains()),
+ fwd("add", ntUnknown, [(ntLitArray, "x"), (ntLitString, "item")], wrapper = getAst arraysAdd()),
+ fwd("shift", ntUnknown, [(ntLitArray, "x")], wrapper = getAst arraysShift()),
+ fwd("pop", ntUnknown, [(ntLitArray, "x")], wrapper = getAst arraysPop()),
+ fwd("shuffle", ntUnknown, [(ntLitArray, "x")], wrapper = getAst arraysShuffle()),
+ fwd("join", ntLitString, [(ntLitArray, "x"), (ntLitString, "sep")], wrapper = getAst arraysJoin()),
+ fwd("delete", ntUnknown, [(ntLitArray, "x"), (ntLitInt, "pos")], wrapper = getAst arraysDelete()),
+ fwd("find", ntLitInt, [(ntLitArray, "x"), (ntLitString, "item")], wrapper = getAst arraysFind()),
+ ]
+ # fnObjects = @[
+ # fwd("hasKey", ntLitBool, [ntLitObject, ntLitString]),
+ # # fwd("keys", ntLitArray, [ntLitObject])
+ # ]
+
+
+ # std/os
+ # implements some read-only basic operating system functions
+ # https://nim-lang.org/docs/os.html
+ template osWalkFiles: untyped =
+ let x = toSeq(walkPattern(args[0].value.sVal))
+ var a = ast.newArray()
+ a.arrayType = ntLitString
+ a.arrayItems =
+ x.map do:
+ proc(xpath: string): Node = ast.newString(xpath)
+ a
+ let
+ fnOs = @[
+ fwd("absolutePath", ntLitString, [(ntLitString, "path")]),
+ fwd("dirExists", ntLitBool, [(ntLitString, "path")]),
+ fwd("fileExists", ntLitBool, [(ntLitString, "path")]),
+ fwd("normalizedPath", ntLitString, [(ntLitString, "path")], "normalize"),
+ # fwd("splitFile", ntTuple, [ntLitString]),
+ fwd("extractFilename", ntLitString, [(ntLitString, "path")], "getFilename"),
+ fwd("isAbsolute", ntLitBool, [(ntLitString, "path")]),
+ fwd("readFile", ntLitString, [(ntLitString, "path")], loadFrom="system"),
+ fwd("isRelativeTo", ntLitBool, [(ntLitString, "path"), (ntLitString, "base")], "isRelative"),
+ fwd("getCurrentDir", ntLitString),
+ fwd("joinPath", ntLitString, [(ntLitString, "head"), (ntLitString, "tail")], "join"),
+ fwd("parentDir", ntLitString, [(ntLitString, "path")]),
+ fwd("walkFiles", ntLitArray, [(ntLitString, "path")], wrapper = getAst osWalkFiles()),
+ ]
+
+ result = newStmtList()
+ let libs = [
+ ("system", fnSystem, "system"),
+ ("math", fnMath, "math"),
+ ("strutils", fnStrings, "strings"),
+ ("sequtils", fnArrays, "arrays"),
+ ("os", fnOs, "os")
+ ]
+ echo "Generate Standard Library"
+ for lib in libs:
+ var sourceCode: string
+ for fn in lib[1]:
+ var
+ lambda = nnkLambda.newTree(newEmptyNode(), newEmptyNode(), newEmptyNode())
+ params = newNimNode(nnkFormalParams)
+ params.add(
+ ident("Node"),
+ nnkIdentDefs.newTree(
+ ident("args"),
+ nnkBracketExpr.newTree(
+ ident("openarray"),
+ ident("Arg")
+ ),
+ newEmptyNode()
+ ),
+ nnkIdentDefs.newTree(
+ ident("returnType"),
+ ident("NodeType"),
+ ident(symbolName(fn.returns))
+ )
+ )
+ lambda.add(params)
+ lambda.add(newEmptyNode())
+ lambda.add(newEmptyNode())
+ var valNode =
+ case fn.returns:
+ of ntLitBool: "newBool"
+ of ntLitString: "newString"
+ of ntLitInt: "newInteger"
+ of ntLitFloat: "newFloat"
+ of ntLitArray: "newArray" # todo implement toArray
+ of ntLitObject: "newObject" # todo implement toObject
+ else: ""
+ var i = 0
+ var fnIdent = if fn.alias.len != 0: fn.alias else: fn.id
+ add sourceCode, addFunction(fnIdent, fn.args, fn.returns)
+ var callNode: NimNode
+ if not fn.hasWrapper:
+ var callableNode =
+ if lib[0] != "system":
+ if fn.loadFrom.len == 0:
+ newCall(newDotExpr(ident(lib[0]), ident(fn.id)))
+ else:
+ newCall(newDotExpr(ident(fn.loadFrom), ident(fn.id)))
+ else:
+ if fn.loadFrom.len == 0:
+ newCall(newDotExpr(ident("system"), ident(fn.id)))
+ else:
+ newCall(newDotExpr(ident(fn.loadFrom), ident(fn.id)))
+ for arg in fn.args:
+ let fieldName =
+ case arg[0]
+ of ntLitBool: "bVal"
+ of ntLitString: "sVal"
+ of ntLitInt: "iVal"
+ of ntLitFloat: "fVal"
+ of ntLitArray: "arrayItems"
+ of ntLitObject: "pairsVal"
+ else: "None"
+ if fieldName.len != 0:
+ callableNode.add(
+ newDotExpr(
+ newDotExpr(
+ nnkBracketExpr.newTree(
+ ident("args"),
+ newLit(i)
+ ),
+ ident("value")
+ ),
+ ident(fieldName)
+ )
+ )
+ else:
+ callableNode.add(
+ newDotExpr(
+ nnkBracketExpr.newTree(
+ ident("args"),
+ newLit(i)
+ ),
+ ident("value")
+ ),
+ )
+ inc i
+ callNode = newCall(ident(valNode), callableNode)
+ else:
+ callNode = fn.wrapper
+ lambda.add(newStmtList(callNode))
+ add result,
+ newAssignment(
+ nnkBracketExpr.newTree(
+ ident(lib[0] & "Module"),
+ newLit(fnIdent)
+ ),
+ lambda
+ )
+ add result,
+ newAssignment(
+ nnkBracketExpr.newTree(
+ ident("stdlib"),
+ newLit("std/" & lib[2])
+ ),
+ nnkTupleConstr.newTree(
+ ident(lib[0] & "Module"),
+ newCall(ident("SourceCode"), newLit(sourceCode))
+ )
+ )
+ when not defined release:
+ echo "std/" & lib[2]
+ echo sourceCode
+
+proc initstdlib*() =
+ {.gcsafe.}:
+ initStandardLibrary()
+
+proc exists*(lib: string): bool =
+ ## Checks if if `lib` exists in `Stdlib`
+ result = stdlib.hasKey(lib)
+
+proc std*(lib: string): (Module, SourceCode) {.raises: KeyError.} =
+ ## Retrieves a module from `Stdlib`
+ result = stdlib[lib]
+
+proc call*(lib, fnName: string, args: seq[Arg]): Node =
+ ## Retrieves a Nim proc from `module`
+ result = stdlib[lib][0][fnName](args)
diff --git a/src/tim/engine/tokens.nim b/src/tim/engine/tokens.nim
index 1973b41..94279ea 100755
--- a/src/tim/engine/tokens.nim
+++ b/src/tim/engine/tokens.nim
@@ -101,6 +101,8 @@ handlers:
lex.token = "@placeholder"
elif lex.next("include"):
lex.setToken tkInclude, 8
+ elif lex.next("import"):
+ lex.setToken tkImport, 7
elif lex.next("view"):
lex.setToken tkViewLoader, 5
elif lex.next("client"):
@@ -207,6 +209,7 @@ registerTokens toktokSettings:
# magics
at = tokenize(handleMagics, '@')
+ `import`
snippetJs
snippetYaml
snippetJson
@@ -215,7 +218,6 @@ registerTokens toktokSettings:
client
`end`
`include`
- `import` = "import"
fn = "fn"
`func` = "func"