Skip to content

Commit ab86bdc

Browse files
committed
update extensions
Signed-off-by: George Lemon <georgelemon@protonmail.com>
1 parent c0bc062 commit ab86bdc

5 files changed

Lines changed: 139 additions & 80 deletions

File tree

src/vancode/interpreter/ast.nim

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,16 +180,17 @@ proc add*(node: Node, children: openarray[Node]): Node {.discardable.} =
180180
proc hash*(node: Node): Hash =
181181
var h = Hash(0)
182182
h = h !& hash(node.kind)
183-
case node.kind
184-
of nkEmpty: discard
185-
of nkBool: h = h !& hash(node.boolVal)
186-
of nkInt: h = h !& hash(node.intVal)
187-
of nkFloat: h = h !& hash(node.floatVal)
188-
of nkString: h = h !& hash(node.stringVal)
189-
of nkIdent: h = h !& hash(node.ident)
190-
else:
191-
h = h !& hash(node.len)
192-
h = h !& hash(node.children)
183+
extendableCase "astHashCase":
184+
case node.kind
185+
of nkEmpty: discard
186+
of nkBool: h = h !& hash(node.boolVal)
187+
of nkInt: h = h !& hash(node.intVal)
188+
of nkFloat: h = h !& hash(node.floatVal)
189+
of nkString: h = h !& hash(node.stringVal)
190+
of nkIdent: h = h !& hash(node.ident)
191+
else:
192+
h = h !& hash(node.len)
193+
h = h !& hash(node.children)
193194
result = h
194195

195196
proc `$`*(node: Node): string =
@@ -374,10 +375,12 @@ proc newStringLit*(val: string): Node =
374375
result = newNode(nkString)
375376
result.stringVal = val
376377

377-
proc newIdent*(ident: string): Node =
378+
proc newIdent*(ident: string, ln, col: int = 0): Node =
378379
## Construct a new ident node.
379380
result = newNode(nkIdent)
380381
result.ident = ident
382+
result.ln = ln
383+
result.col = col
381384

382385
proc newIdentDefs*(names: openarray[Node], ty: Node, value = newEmpty()): Node =
383386
## Construct a new nkIdentDefs node.

src/vancode/interpreter/codegen.nim

Lines changed: 78 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ type
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
@@ -96,7 +96,9 @@ type
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
@@ -109,6 +111,7 @@ type
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

174173
proc 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.}
222221
proc genArray*(node: Node, isInstantiation = false): Sym {.codegen.}
223222
proc genGetField*(node: Node): Sym {.codegen.}
224223
proc genTypeDef*(node: Node): Sym {.codegen.}
224+
proc genFor*(node: Node) {.codegen.}
225+
proc procCall*(node: Node, procSym: Sym): Sym {.codegen.}
225226

226227
let callBuiltinEcho = ast.newCall(ast.newIdent"echo")
227228
# some cached nodes for codegen optimizations
@@ -638,7 +639,7 @@ proc pushVar(gen: CodeGen, sym: Sym) =
638639
proc 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

667674
proc 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+
769794
proc 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

857882
proc 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

11641177
proc 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

14021415
proc 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

16331651
proc 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

Comments
 (0)