Skip to content

Commit

Permalink
stdlib generator [#5]
Browse files Browse the repository at this point in the history
Signed-off-by: George Lemon <[email protected]>
  • Loading branch information
georgelemon committed Mar 25, 2024
1 parent 0fbec7a commit 7e46a4c
Show file tree
Hide file tree
Showing 4 changed files with 506 additions and 38 deletions.
65 changes: 56 additions & 9 deletions src/tim/engine/compilers/html.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down
70 changes: 42 additions & 28 deletions src/tim/engine/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 7e46a4c

Please sign in to comment.