6363 # # to be re-parsed every time they're included
6464 cachedAst* : Table [string , Ast ]
6565
66- ParserCallback * = proc (astProgram: var Ast , path: string )
66+ ParserCallback * = proc (astProgram: var Ast , path: string , resolver: FileResolver )
6767 # # Used to parse custom nodes during code generation.
6868
6969 CodeGen * {.acyclic .} = ref object
9696 iterForBody: Node # the for loop's body
9797 iterForVar: Node # the for loop variable's name
9898 iterForCtx: Context # the for loop's context
99- resolver: FileResolver
99+ resolver* : FileResolver
100+ # # the file resolver used for resolving imports and includes. This is shared
101+ # # between codegen instances to maintain a consistent cache of resolved files.
100102 pkgr: Packager
101103 # the package manager used for resolving packages
102104 parserCallback* : ParserCallback
109111 counter: uint16
110112 # a counter used for generating unique labels and symbols. this is used to
111113 # avoid name collisions when generating code for things like loops and if statements
114+ # instantiationCache: Table[Hash, Sym]
112115
113116 ModuleLibrary = proc (script: Script , systemModule: Module ): Module
114117 # a procedure where we can add FFI procs and types to a module
@@ -160,16 +163,12 @@ proc initCodeGen*(script: Script, module: Module, chunk: Chunk,
160163 chunk: chunk,
161164 kind: kind,
162165 pkgr: pkgr,
163- parserCallback: parserCallback
166+ parserCallback: parserCallback,
164167 )
165168 if ctxAllocator == nil :
166169 result .ctxAllocator = ContextAllocator ()
167170 result .context = result .ctxAllocator.allocCtx ()
168171 result .resolver = initResolver ()
169-
170- # else:
171- # result.ctxAllocator = ctxAllocator
172- # result.context = ctxAllocator.allocCtx()
173172
174173proc clone (gen: CodeGen , kind: GenKind ): CodeGen =
175174 # Clone a code generator, using a different kind for the new one.
@@ -222,6 +221,8 @@ proc genObjectStorage*(node: Node, isInstantiation = false): Sym {.codegen.}
222221proc genArray * (node: Node , isInstantiation = false ): Sym {.codegen .}
223222proc genGetField * (node: Node ): Sym {.codegen .}
224223proc genTypeDef * (node: Node ): Sym {.codegen .}
224+ proc genFor * (node: Node ) {.codegen .}
225+ proc procCall * (node: Node , procSym: Sym ): Sym {.codegen .}
225226
226227let callBuiltinEcho = ast.newCall (ast.newIdent " echo" )
227228 # some cached nodes for codegen optimizations
@@ -638,7 +639,7 @@ proc pushVar(gen: CodeGen, sym: Sym) =
638639proc pushDefault (gen: CodeGen , ty: Sym ) =
639640 # # Push the default value for the type ``ty`` onto the stack.
640641 assert ty.kind == skType, " Only types have default values"
641- assert ty.tyKind notin tyMeta, " meta-types do not represent a value"
642+ # assert ty.tyKind notin tyMeta, "Type `" & $ty.tyKind & "` does not have a default value"
642643 case ty.tyKind
643644 of ttyBool:
644645 gen.chunk.emit (opcPushFalse)
@@ -660,8 +661,14 @@ proc pushDefault(gen: CodeGen, ty: Sym) =
660661 of ttyPointer:
661662 gen.chunk.emit (opcPushPointer)
662663 gen.chunk.emit (uint16 (ttyPointer))
663- # of tyNil:
664- # gen.chunk.emit(opcNoop)
664+ of ttyAny:
665+ gen.chunk.emit (opcPushS)
666+ gen.chunk.emit (gen.chunk.getString (" " ))
667+ of ttyNil:
668+ gen.chunk.emit (opcPushS)
669+ gen.chunk.emit (gen.chunk.getString (" " ))
670+ # gen.chunk.emit(opcPushNil)
671+ # gen.chunk.emit(uint16(0))
665672 else : discard # unreachable
666673
667674proc getDefaultSym * (gen: CodeGen , kind: NodeKind ): Sym =
@@ -766,6 +773,24 @@ proc findOverload*(sym: Sym, args: seq[Sym],
766773 # the error
767774 errorNode.error (ErrTypeMismatchChoice % [paramList, overloadList])
768775
776+ template withBlock * (node: Node , isStmt: bool = false , body: untyped ) =
777+ gen.pushScope ()
778+ for i, s in node:
779+ if isStmt:
780+ # if it's a statement block,
781+ # generate its children normally
782+ gen.genStmt (s)
783+ else :
784+ # otherwise, treat the last statement as
785+ # an expression (and the value of the block)
786+ if i < node.len - 1 :
787+ gen.genStmt (s)
788+ else :
789+ result = gen.genExpr (s)
790+ body
791+ # pop the block's scope
792+ gen.popScope ()
793+
769794proc splitCall * (ast: Node ): tuple [callee: Sym , args: seq [Node ]] {.codegen .} =
770795 # # Splits any call node (prefix, infix, call, dot access, dot call) into a
771796 # # callee (the thing being called) and parameters. The callee is resolved to a
@@ -856,7 +881,7 @@ proc resolveGenerics*(gen: CodeGen, callable: var Sym,
856881
857882proc callProc * (procSym: Sym , argTypes: seq [Sym ],
858883 errorNode: Node = nil ): Sym {.codegen .} =
859- # # Generate code that calls a procedure. `` errorNode` `
884+ # # Generate code that calls a procedure. `errorNode`
860885 # # is used for error reporting.
861886 if procSym.kind in {skProc, skChoice}:
862887 # find the overload
@@ -887,7 +912,10 @@ proc callProc*(procSym: Sym, argTypes: seq[Sym],
887912 else : gen.chunk.file # fallback to current file
888913 gen.chunk.emit (gen.chunk.getString (theSource))
889914 gen.chunk.emit (theProc.procId)
915+
916+ # set the result type
890917 result = theProc.procReturnTy
918+
891919 elif procSym.kind in skVars:
892920 discard # TODO : call through reference in variable
893921 # elif procSym.kind == skHtmlType:
@@ -1136,30 +1164,15 @@ proc objConstr*(node: Node, ty: Sym, constructFromIdent = false): Sym {.codegen.
11361164 gen.chunk.emit (opcConstrObj)
11371165 gen.chunk.emit (uint16 (emittedCount))
11381166
1139- proc procCall * (node: Node , procSym: Sym ): Sym {.codegen .} =
1140- # # Generate code for a procedure call.
1141- # we simply push all the arguments onto the stack
1142- var argTypes: seq [Sym ]
1143- # if node[^1].kind in {nkHtmlElement, nkIf, nkFor, nkCall}:
1144- # # if the last argument is an HTML element
1145- # # it is a macro call, so we need to
1146- # # push the HTML element onto the stack
1147- # for arg in node[1..^2]:
1148- # let argSym: Sym = gen.genExpr(arg)
1149- # assert argSym != nil, "Expression must return a symbol"
1150- # argTypes.add(argSym)
1151- # # the last argument is a HTML element, so we need to
1152- # # create a symbol for it and add it to the argument types
1153- # let anyStmt = gen.module.sym"stmt" # gen.genExpr(node[^1])
1154- # anyStmt.impl = node[^1]
1155- # argTypes.add(anyStmt)
1156- # else:
1157- for arg in node[1 ..^ 1 ]:
1158- let argSym: Sym = gen.genExpr (arg)
1159- assert argSym != nil , " Expression must return a symbol"
1160- argTypes.add (argSym)
1161- # ...and delegate the call to callProc
1162- result = gen.callProc (procSym, argTypes, errorNode = node)
1167+ when not compiles (procCall):
1168+ proc procCall * (node: Node , procSym: Sym ): Sym {.codegen .} =
1169+ # # Generate code for a procedure call
1170+ var argTypes: seq [Sym ]
1171+ for arg in node[1 ..^ 1 ]:
1172+ let argSym: Sym = gen.genExpr (arg)
1173+ assert argSym != nil , " Expression must return a symbol"
1174+ argTypes.add (argSym)
1175+ return gen.callProc (procSym, argTypes, errorNode = node)
11631176
11641177proc call * (node: Node ): Sym {.codegen .} =
11651178 # # Generates code for an nkCall (proc call or object constructor).
@@ -1400,9 +1413,10 @@ proc genParam(name: Node, ty: Sym, sym: Sym = nil, isMut, isOpt = false): ProcPa
14001413 (name, ty, sym, isMut, isOpt)
14011414
14021415proc collectParams * (formalParams: Node ,
1403- genericParams: Option [seq [Sym ]] = none (seq [Sym ])): seq [ProcParam ] {.codegen .} =
1416+ genericParams: Option [seq [Sym ]] = none (seq [Sym ])): seq [ProcParam ] {.codegen .} =
14041417 # Helper used to collect parameters from an
14051418 # `nkFormalParams` to a `seq[ProcParam]`
1419+ if formalParams.len == 0 : return
14061420 for defs in formalParams[1 ..^ 1 ]:
14071421 let
14081422 rawTyNode = defs[^ 2 ]
@@ -1451,7 +1465,6 @@ proc collectParams*(formalParams: Node,
14511465 newIdent (" __default_" & defName & " _" & $ gen.count ()),
14521466 impl = defaultNode
14531467 )
1454-
14551468 result .add (
14561469 genParam (
14571470 name,
@@ -1510,8 +1523,7 @@ proc genProc*(node: Node, isInstantiation = false): Sym {.codegen.} =
15101523 # we need to do this here, otherwise recursive
15111524 # calls will be broken
15121525 gen.addSym (sym, scopeOffset = ord (sym.genericParams.isSome))
1513- # if we're in an instantiation or the proc is
1514- # not generic, generate its code
1526+
15151527 if not sym.isGeneric or isInstantiation:
15161528 var
15171529 chunk = newChunk (gen.chunk.file)
@@ -1525,26 +1537,25 @@ proc genProc*(node: Node, isInstantiation = false): Sym {.codegen.} =
15251537 procGen.procReturnTy = returnTy
15261538
15271539 # add the proc's parameters as locals
1528- # TODO : closures and upvalues
1529- if params.len > 0 :
1530- procGen.pushScope ()
1531- for (name, ty, implSym, isMut, isOpt) in params:
1532- var varType =
1533- if isMut: skVar # value is mutable
1534- else : skLet # value is immutable
1535- let param = procGen.declareVar (name, varType, ty)
1536- param.varSet = true # arguments are not assignable
1540+ procGen.pushScope ()
1541+ for (name, ty, implSym, isMut, isOpt) in params:
1542+ var varType =
1543+ if isMut: skVar # value is mutable
1544+ else : skLet # value is immutable
1545+ let param = procGen.declareVar (name, varType, ty)
1546+ param.varSet = true # arguments are not assignable
15371547
15381548 # declare ``result`` if applicable
15391549 if returnTy.tyKind != ttyVoid:
15401550 let res = newIdent (" result" )
15411551 procGen.declareVar (res, skVar, returnTy, isMagic = true )
15421552 procGen.pushDefault (returnTy)
15431553 procGen.popVar (res)
1544-
1554+
15451555 # add the proc into the script
15461556 gen.script.procs.add (theProc)
15471557 if sym.procExport:
1558+ # export the proc if needed (exported procs need to be in procsExport for the runtime to find them, but they also need to be in procs for the compiler to compile them, so we add them to both)
15481559 gen.script.procsExport.add (theProc)
15491560
15501561 # compile the proc's body
@@ -1558,6 +1569,7 @@ proc genProc*(node: Node, isInstantiation = false): Sym {.codegen.} =
15581569 procGen.chunk.emit (opcReturnVal)
15591570 else :
15601571 procGen.chunk.emit (opcReturnVoid)
1572+ procGen.popScope ()
15611573 else :
15621574 # add the proc into the script
15631575 gen.script.procs.add (theProc)
@@ -1584,8 +1596,6 @@ proc genExpr*(node: Node, varUnwrap = true): Sym {.codegen.} =
15841596 case node.kind
15851597 of nkBool, nkInt, nkFloat, nkString, nkNil: # constants
15861598 result = gen.pushConst (node)
1587- # of nkHtmlElement:
1588- # result = gen.htmlConstr(node)
15891599 of nkIdent: # variables
15901600 var symNode = gen.lookup (node)
15911601 case symNode.kind:
@@ -1602,10 +1612,9 @@ proc genExpr*(node: Node, varUnwrap = true): Sym {.codegen.} =
16021612 if varUnwrap: symNode.varTy
16031613 else : symNode
16041614 )
1605- of nkPrefix: # prefix operators
1615+ of nkPrefix:
16061616 result = gen.prefix (node)
16071617 of nkInfix:
1608- # handle infix expressions
16091618 result = gen.infix (node)
16101619 of nkDot:
16111620 # handle field access using dot notation `$a.b`
@@ -1622,12 +1631,21 @@ proc genExpr*(node: Node, varUnwrap = true): Sym {.codegen.} =
16221631 of nkObjectStorage:
16231632 result = gen.genObjectStorage (node)
16241633 of nkObject:
1625- # object literal
16261634 result = gen.genObject (node)
16271635 of nkProc:
1628- # procedure definition
16291636 result = gen.genProc (node)
16301637 else :
1638+ # handle statement-like nodes used as lazy-injected macro bodies
1639+ if node.kind in {nkFor, nkWhile, nkIf, nkBlock, nkHtmlElement,
1640+ nkMacro, nkClientBlock, nkViewLoader}:
1641+ # emit the statement into the current chunk (this will produce the
1642+ # code the macro expects as the default 'body' param)
1643+ discard gen.genBlock (node, isStmt = true )
1644+ if gen.module.sym " stmt" .isNil:
1645+ return gen.module.sym " any"
1646+ return gen.module.sym " stmt"
1647+
1648+ debugEcho " Unsupported node kind in genExpr: " & $ node.kind
16311649 node.error (ErrValueIsVoid )
16321650
16331651proc tryElideWhile * (node: Node ): bool {.codegen .} =
@@ -1640,7 +1658,6 @@ proc tryElideWhile*(node: Node): bool {.codegen.} =
16401658 # # Also allows extra trivially-dead local var/let/const declarations
16411659 # # in the loop body (e.g. `var x = 0`), as long as they don't affect control flow.
16421660 if node.len < 2 : return false
1643-
16441661 let
16451662 cond = node[0 ]
16461663 body = node[1 ]
@@ -1840,9 +1857,9 @@ proc genWhile*(node: Node) {.codegen.} =
18401857 gen.chunk.emit (opcJumpBack)
18411858 gen.chunk.emit (uint16 (gen.chunk.code.len - beforeLoop - 1 ))
18421859 if not isWhileTrue:
1843- # if it wasn't a while true, we need to fill in the hole after the loop
1860+ # if it wasn't a while true, we need to fill in
1861+ # the hole after the loop
18441862 gen.chunk.patchHole (afterLoop)
1845- # ...and pop the condition off the stack after the loop is done
18461863 gen.chunk.emit (opcDiscard)
18471864 gen.chunk.emit (1 'u8 )
18481865
@@ -1857,9 +1874,6 @@ proc genFor*(node: Node) {.codegen.} =
18571874 # # All a ``for`` loop does is it walks the body of the iterator and replaces
18581875 # # any ``yield``s with the for loop's body.
18591876
1860- # this is some really fragile stuff, I wouldn't be surprised if it contains
1861- # like, a million bugs
1862-
18631877 # as of now, only one loop variable is supported
18641878 # this will be changed when tuples are introduced
18651879 let
@@ -2291,7 +2305,8 @@ proc genImport*(node: Node) {.codegen.} =
22912305 astProgram = codegenCache.cachedAst[path]
22922306 else :
22932307 # parse the module's source code into an AST
2294- gen.parserCallback (astProgram, path)
2308+ # pass the active resolver so the callback can read from VFS
2309+ gen.parserCallback (astProgram, path, gen.resolver)
22952310
22962311 var
22972312 importChunk = newChunk (astProgram.sourcePath)
@@ -2341,7 +2356,7 @@ proc genImport*(node: Node) {.codegen.} =
23412356 astProgram = codegenCache.cachedAst[path]
23422357 else :
23432358 # parse the module's source code into an AST
2344- gen.parserCallback (astProgram, path)
2359+ gen.parserCallback (astProgram, path, gen.resolver )
23452360 for n in astProgram.nodes:
23462361 gen.genStmt (n)
23472362 else : discard
0 commit comments