Merge branch 'master' into asyncmacro
This commit is contained in:
commit
3e7ea859ec
28 changed files with 2485 additions and 19 deletions
|
|
@ -488,7 +488,7 @@ type
|
||||||
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
|
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
|
||||||
mNBindSym, mLocals, mNCallSite,
|
mNBindSym, mLocals, mNCallSite,
|
||||||
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
|
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
|
||||||
mInstantiationInfo, mGetTypeInfo
|
mInstantiationInfo, mGetTypeInfo, mNGenSym
|
||||||
|
|
||||||
# things that we can evaluate safely at compile time, even if not asked for it:
|
# things that we can evaluate safely at compile time, even if not asked for it:
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -1249,6 +1249,16 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||||
of mNBindSym:
|
of mNBindSym:
|
||||||
# trivial implementation:
|
# trivial implementation:
|
||||||
result = n.sons[1]
|
result = n.sons[1]
|
||||||
|
of mNGenSym:
|
||||||
|
evalX(n.sons[1], {efLValue})
|
||||||
|
let k = getOrdValue(result)
|
||||||
|
evalX(n.sons[2], {efLValue})
|
||||||
|
let b = result
|
||||||
|
let name = if b.strVal.len == 0: ":tmp" else: b.strVal
|
||||||
|
if k < 0 or k > ord(high(TSymKind)):
|
||||||
|
internalError(n.info, "request to create a symbol with invalid kind")
|
||||||
|
result = newSymNode(newSym(k.TSymKind, name.getIdent, c.module, n.info))
|
||||||
|
incl(result.sym.flags, sfGenSym)
|
||||||
of mStrToIdent:
|
of mStrToIdent:
|
||||||
result = evalAux(c, n.sons[1], {})
|
result = evalAux(c, n.sons[1], {})
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
|
|
|
||||||
33
compiler/nimeval.nim
Normal file
33
compiler/nimeval.nim
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nimrod Compiler
|
||||||
|
# (c) Copyright 2013 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## exposes the Nimrod VM to clients.
|
||||||
|
|
||||||
|
import
|
||||||
|
ast, modules, passes, passaux, condsyms,
|
||||||
|
options, nimconf, lists, sem, semdata, llstream, vm
|
||||||
|
|
||||||
|
proc execute*(program: string) =
|
||||||
|
passes.gIncludeFile = includeModule
|
||||||
|
passes.gImportModule = importModule
|
||||||
|
initDefines()
|
||||||
|
LoadConfigs(DefaultConfig)
|
||||||
|
|
||||||
|
initDefines()
|
||||||
|
DefineSymbol("nimrodvm")
|
||||||
|
when hasFFI: DefineSymbol("nimffi")
|
||||||
|
registerPass(verbosePass)
|
||||||
|
registerPass(semPass)
|
||||||
|
registerPass(vmPass)
|
||||||
|
|
||||||
|
appendStr(searchPaths, options.libpath)
|
||||||
|
compileSystemModule()
|
||||||
|
var m = makeStdinModule()
|
||||||
|
incl(m.flags, sfMainModule)
|
||||||
|
processModule(m, LLStreamOpen(program), nil)
|
||||||
|
|
@ -625,6 +625,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||||
result = newIntNodeT(sonsLen(a), n)
|
result = newIntNodeT(sonsLen(a), n)
|
||||||
else:
|
else:
|
||||||
result = magicCall(m, n)
|
result = magicCall(m, n)
|
||||||
|
of mLengthArray:
|
||||||
|
# It doesn't matter if the argument is const or not for mLengthArray.
|
||||||
|
# This fixes bug #544.
|
||||||
|
result = newIntNodeT(lengthOrd(n.sons[1].typ), n)
|
||||||
of mAstToStr:
|
of mAstToStr:
|
||||||
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
|
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
|
||||||
of mConStrStr:
|
of mConStrStr:
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
|
n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
of nkPragma, nkPragmaExpr: nil
|
of nkPragma, nkPragmaExpr: nil
|
||||||
of nkExprColonExpr:
|
of nkExprColonExpr, nkExprEqExpr:
|
||||||
checkMinSonsLen(n, 2)
|
checkMinSonsLen(n, 2)
|
||||||
result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
|
result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -841,7 +841,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
LocalError(n.info, errTypeExpected)
|
LocalError(n.info, errTypeExpected)
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
if n[0].kind == nkIdent:
|
if isRange(n):
|
||||||
|
result = semRangeAux(c, n, prev)
|
||||||
|
elif n[0].kind == nkIdent:
|
||||||
let op = n.sons[0].ident
|
let op = n.sons[0].ident
|
||||||
if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
|
if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
|
||||||
checkSonsLen(n, 3)
|
checkSonsLen(n, 3)
|
||||||
|
|
|
||||||
|
|
@ -959,8 +959,8 @@ proc skipGenericAlias*(t: PType): PType =
|
||||||
proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool =
|
proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool =
|
||||||
for i in countup(0, typeClass.sonsLen - 1):
|
for i in countup(0, typeClass.sonsLen - 1):
|
||||||
let req = typeClass.sons[i]
|
let req = typeClass.sons[i]
|
||||||
var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind
|
var match = req.kind == skipTypes(t, {tyGenericInst, tyRange}).kind or
|
||||||
|
req.kind == skipTypes(t, {tyGenericInst}).kind
|
||||||
if not match:
|
if not match:
|
||||||
case req.kind
|
case req.kind
|
||||||
of tyGenericBody:
|
of tyGenericBody:
|
||||||
|
|
|
||||||
584
compiler/vm.nim
Normal file
584
compiler/vm.nim
Normal file
|
|
@ -0,0 +1,584 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nimrod Compiler
|
||||||
|
# (c) Copyright 2013 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This file implements the new evaluation engine for Nimrod code.
|
||||||
|
## An instruction is 1-2 int32s in memory, it is a register based VM.
|
||||||
|
|
||||||
|
import ast, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned
|
||||||
|
|
||||||
|
type
|
||||||
|
PStackFrame* = ref TStackFrame
|
||||||
|
TStackFrame* = object
|
||||||
|
prc: PSym # current prc; proc that is evaluated
|
||||||
|
slots: TNodeSeq # parameters passed to the proc + locals;
|
||||||
|
# parameters come first
|
||||||
|
next: PStackFrame # for stacking
|
||||||
|
comesFrom: int
|
||||||
|
safePoints: seq[int] # used for exception handling
|
||||||
|
# XXX 'break' should perform cleanup actions
|
||||||
|
# What does the C backend do for it?
|
||||||
|
|
||||||
|
proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int) =
|
||||||
|
if x != nil:
|
||||||
|
stackTraceAux(c, x.next, x.comesFrom)
|
||||||
|
var info = c.debug[pc]
|
||||||
|
# we now use the same format as in system/except.nim
|
||||||
|
var s = toFilename(info)
|
||||||
|
var line = toLineNumber(info)
|
||||||
|
if line > 0:
|
||||||
|
add(s, '(')
|
||||||
|
add(s, $line)
|
||||||
|
add(s, ')')
|
||||||
|
if x.prc != nil:
|
||||||
|
for k in 1..max(1, 25-s.len): add(s, ' ')
|
||||||
|
add(s, x.prc.name.s)
|
||||||
|
MsgWriteln(s)
|
||||||
|
|
||||||
|
proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
|
||||||
|
msg: TMsgKind, arg = "") =
|
||||||
|
MsgWriteln("stack trace: (most recent call last)")
|
||||||
|
stackTraceAux(c, tos, pc)
|
||||||
|
LocalError(c.debug[pc], msg, arg)
|
||||||
|
|
||||||
|
proc bailOut(c: PCtx; tos: PStackFrame) =
|
||||||
|
stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
|
||||||
|
c.currentExceptionA.sons[2].strVal)
|
||||||
|
|
||||||
|
when not defined(nimHasInterpreterLoop):
|
||||||
|
{.pragma: interpreterLoop.}
|
||||||
|
|
||||||
|
template inc(pc: ptr TInstr, diff = 1) =
|
||||||
|
inc cast[TAddress](pc), TInstr.sizeof * diff
|
||||||
|
|
||||||
|
template ensureKind(k: expr) {.immediate, dirty.} =
|
||||||
|
if regs[ra].kind != k:
|
||||||
|
myreset(regs[ra])
|
||||||
|
regs[ra].kind = k
|
||||||
|
|
||||||
|
template decodeB(k: expr) {.immediate, dirty.} =
|
||||||
|
let rb = instr.regB
|
||||||
|
ensureKind(k)
|
||||||
|
|
||||||
|
template decodeBC(k: expr) {.immediate, dirty.} =
|
||||||
|
let rb = instr.regB
|
||||||
|
let rc = instr.regC
|
||||||
|
ensureKind(k)
|
||||||
|
|
||||||
|
template decodeBImm(k: expr) {.immediate, dirty.} =
|
||||||
|
let rb = instr.regB
|
||||||
|
let imm = instr.regC - byteExcess
|
||||||
|
ensureKind(k)
|
||||||
|
|
||||||
|
template decodeBx(k: expr) {.immediate, dirty.} =
|
||||||
|
let rbx = instr.regBx - wordExcess
|
||||||
|
ensureKind(k)
|
||||||
|
|
||||||
|
proc compile(c: PCtx, s: PSym): int = vmgen.genProc(c, s)
|
||||||
|
|
||||||
|
proc myreset(n: PNode) =
|
||||||
|
when defined(system.reset):
|
||||||
|
var oldInfo = n.info
|
||||||
|
reset(n[])
|
||||||
|
n.info = oldInfo
|
||||||
|
|
||||||
|
template move(a, b: expr) = system.shallowCopy(a, b)
|
||||||
|
# XXX fix minor 'shallowCopy' overloading bug in compiler
|
||||||
|
|
||||||
|
proc asgnRef(x, y: PNode) =
|
||||||
|
myreset(x)
|
||||||
|
x.kind = y.kind
|
||||||
|
x.typ = y.typ
|
||||||
|
case x.kind
|
||||||
|
of nkCharLit..nkInt64Lit: x.intVal = y.intVal
|
||||||
|
of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal
|
||||||
|
of nkStrLit..nkTripleStrLit: x.strVal = y.strVal
|
||||||
|
of nkIdent: x.ident = y.ident
|
||||||
|
of nkSym: x.sym = y.sym
|
||||||
|
else:
|
||||||
|
if x.kind notin {nkEmpty..nkNilLit}:
|
||||||
|
move(x.sons, y.sons)
|
||||||
|
|
||||||
|
proc asgnComplex(x, y: PNode) =
|
||||||
|
myreset(x)
|
||||||
|
x.kind = y.kind
|
||||||
|
x.typ = y.typ
|
||||||
|
case x.kind
|
||||||
|
of nkCharLit..nkInt64Lit: x.intVal = y.intVal
|
||||||
|
of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal
|
||||||
|
of nkStrLit..nkTripleStrLit: x.strVal = y.strVal
|
||||||
|
of nkIdent: x.ident = y.ident
|
||||||
|
of nkSym: x.sym = y.sym
|
||||||
|
else:
|
||||||
|
if x.kind notin {nkEmpty..nkNilLit}:
|
||||||
|
let y = y.copyTree
|
||||||
|
for i in countup(0, sonsLen(y) - 1): addSon(x, y.sons[i])
|
||||||
|
|
||||||
|
template getstr(a: expr): expr =
|
||||||
|
(if a.kind == nkStrLit: a.strVal else: $chr(int(a.intVal)))
|
||||||
|
|
||||||
|
proc pushSafePoint(f: PStackFrame; pc: int) =
|
||||||
|
if f.safePoints.isNil: f.safePoints = @[]
|
||||||
|
f.safePoints.add(pc)
|
||||||
|
|
||||||
|
proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop()
|
||||||
|
|
||||||
|
proc nextSafePoint(f: PStackFrame): int =
|
||||||
|
var f = f
|
||||||
|
while f.safePoints.isNil or f.safePoints.len == 0:
|
||||||
|
f = f.next
|
||||||
|
if f.isNil: return -1
|
||||||
|
result = f.safePoints.pop
|
||||||
|
|
||||||
|
proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: TNodeSeq): int =
|
||||||
|
let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs)
|
||||||
|
while true:
|
||||||
|
var pc2 = tos.nextSafePoint
|
||||||
|
if pc2 == -1: return -1
|
||||||
|
|
||||||
|
var nextExceptOrFinally = -1
|
||||||
|
if c.code[pc2].opcode == opcExcept:
|
||||||
|
nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess
|
||||||
|
inc pc2
|
||||||
|
while c.code[pc2].opcode == opcExcept:
|
||||||
|
let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes(
|
||||||
|
abstractPtrs)
|
||||||
|
if inheritanceDiff(exceptType, raisedType) <= 0:
|
||||||
|
# mark exception as handled but keep it in B for
|
||||||
|
# the getCurrentException() builtin:
|
||||||
|
c.currentExceptionB = c.currentExceptionA
|
||||||
|
c.currentExceptionA = nil
|
||||||
|
# execute the corresponding handler:
|
||||||
|
return pc2
|
||||||
|
inc pc2
|
||||||
|
if nextExceptOrFinally >= 0:
|
||||||
|
pc2 = nextExceptOrFinally
|
||||||
|
if c.code[pc2].opcode == opcFinally:
|
||||||
|
# execute the corresponding handler, but don't quit walking the stack:
|
||||||
|
return pc2
|
||||||
|
|
||||||
|
proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int =
|
||||||
|
if f.safePoints.isNil: return -1
|
||||||
|
for s in f.safePoints:
|
||||||
|
var pc = s
|
||||||
|
while c.code[pc].opcode == opcExcept:
|
||||||
|
pc = pc + c.code[pc].regBx - wordExcess
|
||||||
|
if c.code[pc].opcode == opcFinally:
|
||||||
|
return pc
|
||||||
|
return -1
|
||||||
|
|
||||||
|
proc execute(c: PCtx, start: int) =
|
||||||
|
var pc = start
|
||||||
|
var regs: TNodeSeq # alias to tos.slots for performance
|
||||||
|
var tos: PStackFrame
|
||||||
|
newSeq(regs, c.prc.maxSlots)
|
||||||
|
while true:
|
||||||
|
{.interpreterLoop.}
|
||||||
|
let instr = c.code[pc]
|
||||||
|
let ra = instr.regA
|
||||||
|
echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
|
||||||
|
case instr.opcode
|
||||||
|
of opcEof: break
|
||||||
|
of opcRet:
|
||||||
|
# XXX perform any cleanup actions
|
||||||
|
tos = tos.next
|
||||||
|
if tos.isNil: return
|
||||||
|
let retVal = regs[0]
|
||||||
|
move(regs, tos.slots)
|
||||||
|
pc = tos.comesFrom
|
||||||
|
assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn}
|
||||||
|
if c.code[pc].opcode == opcIndCallAsgn:
|
||||||
|
regs[c.code[pc].regA] = retVal
|
||||||
|
of opcYldYoid: assert false
|
||||||
|
of opcYldVal: assert false
|
||||||
|
of opcAsgnInt:
|
||||||
|
echo ra, " ", instr.regB, " ", regs.len, tos.prc.name.s
|
||||||
|
decodeB(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal
|
||||||
|
of opcAsgnStr:
|
||||||
|
decodeB(nkStrLit)
|
||||||
|
debug regs[rb]
|
||||||
|
echo rb
|
||||||
|
Message(c.debug[pc], warnUser, " here")
|
||||||
|
regs[ra].strVal = regs[rb].strVal
|
||||||
|
of opcAsgnFloat:
|
||||||
|
decodeB(nkFloatLit)
|
||||||
|
regs[ra].floatVal = regs[rb].floatVal
|
||||||
|
of opcAsgnComplex:
|
||||||
|
asgnComplex(regs[ra], regs[instr.regB])
|
||||||
|
of opcAsgnRef:
|
||||||
|
asgnRef(regs[ra], regs[instr.regB])
|
||||||
|
of opcWrGlobalRef:
|
||||||
|
asgnRef(c.globals[instr.regBx-wordExcess-1], regs[ra])
|
||||||
|
of opcWrGlobal:
|
||||||
|
asgnComplex(c.globals.sons[instr.regBx-wordExcess-1], regs[ra])
|
||||||
|
of opcLdArr:
|
||||||
|
# a = b[c]
|
||||||
|
let rb = instr.regB
|
||||||
|
let rc = instr.regC
|
||||||
|
let idx = regs[rc].intVal
|
||||||
|
# XXX what if the array is not 0-based? -> codegen should insert a sub
|
||||||
|
regs[ra] = regs[rb].sons[idx.int]
|
||||||
|
of opcWrArr:
|
||||||
|
# a[b] = c
|
||||||
|
let rb = instr.regB
|
||||||
|
let rc = instr.regC
|
||||||
|
let idx = regs[rb].intVal
|
||||||
|
asgnComplex(regs[ra].sons[idx.int], regs[rc])
|
||||||
|
of opcWrArrRef:
|
||||||
|
let rb = instr.regB
|
||||||
|
let rc = instr.regC
|
||||||
|
let idx = regs[rb].intVal
|
||||||
|
asgnRef(regs[ra].sons[idx.int], regs[rc])
|
||||||
|
of opcLdObj:
|
||||||
|
# a = b.c
|
||||||
|
let rb = instr.regB
|
||||||
|
let rc = instr.regC
|
||||||
|
# XXX this creates a wrong alias
|
||||||
|
asgnComplex(regs[ra], regs[rb].sons[rc])
|
||||||
|
of opcWrObj:
|
||||||
|
# a.b = c
|
||||||
|
let rb = instr.regB
|
||||||
|
let rc = instr.regC
|
||||||
|
asgnComplex(regs[ra].sons[rb], regs[rc])
|
||||||
|
of opcWrObjRef:
|
||||||
|
let rb = instr.regB
|
||||||
|
let rc = instr.regC
|
||||||
|
asgnRef(regs[ra].sons[rb], regs[rc])
|
||||||
|
of opcWrStrIdx:
|
||||||
|
decodeBC(nkStrLit)
|
||||||
|
let idx = regs[rb].intVal.int
|
||||||
|
regs[ra].strVal[idx] = chr(regs[rc].intVal)
|
||||||
|
of opcAddr:
|
||||||
|
decodeB(nkRefTy)
|
||||||
|
if regs[ra].len == 0: regs[ra].add regs[rb]
|
||||||
|
else: regs[ra].sons[0] = regs[rb]
|
||||||
|
of opcDeref:
|
||||||
|
# a = b[]
|
||||||
|
let rb = instr.regB
|
||||||
|
if regs[rb].kind == nkNilLit:
|
||||||
|
stackTrace(c, tos, pc, errNilAccess)
|
||||||
|
assert regs[rb].kind == nkRefTy
|
||||||
|
regs[ra] = regs[rb].sons[0]
|
||||||
|
of opcAddInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal + regs[rc].intVal
|
||||||
|
of opcAddImmInt:
|
||||||
|
decodeBImm(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal + imm
|
||||||
|
of opcSubInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal - regs[rc].intVal
|
||||||
|
of opcSubImmInt:
|
||||||
|
decodeBImm(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal - imm
|
||||||
|
of opcLenSeq:
|
||||||
|
decodeBImm(nkIntLit)
|
||||||
|
assert regs[rb].kind == nkBracket
|
||||||
|
regs[ra].intVal = regs[rb].len - imm
|
||||||
|
of opcLenStr:
|
||||||
|
decodeBImm(nkIntLit)
|
||||||
|
assert regs[rb].kind == nkStrLit
|
||||||
|
regs[ra].intVal = regs[rb].strVal.len - imm
|
||||||
|
of opcIncl:
|
||||||
|
decodeB(nkCurly)
|
||||||
|
if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb]))
|
||||||
|
of opcExcl:
|
||||||
|
decodeB(nkCurly)
|
||||||
|
# XXX arg we need types here :-(
|
||||||
|
var b = newNodeIT(nkCurly, regs[rb].info, regs[rb].typ)
|
||||||
|
addSon(b, regs[rb])
|
||||||
|
var r = diffSets(regs[ra], b)
|
||||||
|
discardSons(regs[ra])
|
||||||
|
for i in countup(0, sonsLen(r) - 1): addSon(regs[ra], r.sons[i])
|
||||||
|
of opcCard:
|
||||||
|
decodeB(nkIntLit)
|
||||||
|
regs[ra].intVal = nimsets.cardSet(regs[rb])
|
||||||
|
of opcMulInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal * regs[rc].intVal
|
||||||
|
of opcDivInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal div regs[rc].intVal
|
||||||
|
of opcModInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal
|
||||||
|
of opcAddFloat:
|
||||||
|
decodeBC(nkFloatLit)
|
||||||
|
regs[ra].floatVal = regs[rb].floatVal + regs[rc].floatVal
|
||||||
|
of opcSubFloat:
|
||||||
|
decodeBC(nkFloatLit)
|
||||||
|
regs[ra].floatVal = regs[rb].floatVal - regs[rc].floatVal
|
||||||
|
of opcMulFloat:
|
||||||
|
decodeBC(nkFloatLit)
|
||||||
|
regs[ra].floatVal = regs[rb].floatVal * regs[rc].floatVal
|
||||||
|
of opcDivFloat:
|
||||||
|
decodeBC(nkFloatLit)
|
||||||
|
regs[ra].floatVal = regs[rb].floatVal / regs[rc].floatVal
|
||||||
|
of opcShrInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal shr regs[rc].intVal
|
||||||
|
of opcShlInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal
|
||||||
|
of opcBitandInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal and regs[rc].intVal
|
||||||
|
of opcBitorInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal or regs[rc].intVal
|
||||||
|
of opcBitxorInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal xor regs[rc].intVal
|
||||||
|
of opcAddu:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal +% regs[rc].intVal
|
||||||
|
of opcSubu:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal
|
||||||
|
of opcMulu:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal
|
||||||
|
of opcDivu:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal /% regs[rc].intVal
|
||||||
|
of opcModu:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = regs[rb].intVal %% regs[rc].intVal
|
||||||
|
of opcEqInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].intVal == regs[rc].intVal)
|
||||||
|
of opcLeInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].intVal <= regs[rc].intVal)
|
||||||
|
of opcLtInt:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].intVal < regs[rc].intVal)
|
||||||
|
of opcEqFloat:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].floatVal == regs[rc].floatVal)
|
||||||
|
of opcLeFloat:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].floatVal <= regs[rc].floatVal)
|
||||||
|
of opcLtFloat:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].floatVal < regs[rc].floatVal)
|
||||||
|
of opcLeu:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].intVal <=% regs[rc].intVal)
|
||||||
|
of opcLtu:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal)
|
||||||
|
of opcEqRef:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb] == regs[rc])
|
||||||
|
# XXX is this correct? nope ...
|
||||||
|
of opcXor:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal)
|
||||||
|
of opcNot:
|
||||||
|
decodeB(nkIntLit)
|
||||||
|
assert regs[rb].kind == nkIntLit
|
||||||
|
regs[ra].intVal = 1 - regs[rb].intVal
|
||||||
|
of opcUnaryMinusInt:
|
||||||
|
decodeB(nkIntLit)
|
||||||
|
assert regs[rb].kind == nkIntLit
|
||||||
|
regs[ra].intVal = -regs[rb].intVal
|
||||||
|
of opcUnaryMinusFloat:
|
||||||
|
decodeB(nkFloatLit)
|
||||||
|
assert regs[rb].kind == nkFloatLit
|
||||||
|
regs[ra].floatVal = -regs[rb].floatVal
|
||||||
|
of opcBitnotInt:
|
||||||
|
decodeB(nkIntLit)
|
||||||
|
assert regs[rb].kind == nkIntLit
|
||||||
|
regs[ra].intVal = not regs[rb].intVal
|
||||||
|
of opcEqStr:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = Ord(regs[rb].strVal == regs[rc].strVal)
|
||||||
|
of opcLeStr:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = Ord(regs[rb].strVal <= regs[rc].strVal)
|
||||||
|
of opcLtStr:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = Ord(regs[rb].strVal < regs[rc].strVal)
|
||||||
|
of opcLeSet:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = Ord(containsSets(regs[rb], regs[rc]))
|
||||||
|
of opcEqSet:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = Ord(equalSets(regs[rb], regs[rc]))
|
||||||
|
of opcLtSet:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
let a = regs[rb]
|
||||||
|
let b = regs[rc]
|
||||||
|
regs[ra].intVal = Ord(containsSets(a, b) and not equalSets(a, b))
|
||||||
|
of opcMulSet:
|
||||||
|
decodeBC(nkCurly)
|
||||||
|
move(regs[ra].sons, nimsets.intersectSets(regs[rb], regs[rc]).sons)
|
||||||
|
of opcPlusSet:
|
||||||
|
decodeBC(nkCurly)
|
||||||
|
move(regs[ra].sons, nimsets.unionSets(regs[rb], regs[rc]).sons)
|
||||||
|
of opcMinusSet:
|
||||||
|
decodeBC(nkCurly)
|
||||||
|
move(regs[ra].sons, nimsets.diffSets(regs[rb], regs[rc]).sons)
|
||||||
|
of opcSymDiffSet:
|
||||||
|
decodeBC(nkCurly)
|
||||||
|
move(regs[ra].sons, nimsets.symdiffSets(regs[rb], regs[rc]).sons)
|
||||||
|
of opcConcatStr:
|
||||||
|
decodeBC(nkStrLit)
|
||||||
|
regs[ra].strVal = getstr(regs[rb])
|
||||||
|
for i in rb+1..rb+rc-1:
|
||||||
|
regs[ra].strVal.add getstr(regs[i])
|
||||||
|
of opcEcho:
|
||||||
|
echo regs[ra].strVal
|
||||||
|
of opcContainsSet:
|
||||||
|
decodeBC(nkIntLit)
|
||||||
|
regs[ra].intVal = Ord(inSet(regs[rb], regs[rc]))
|
||||||
|
of opcSubStr:
|
||||||
|
decodeBC(nkStrLit)
|
||||||
|
inc pc
|
||||||
|
assert c.code[pc].opcode == opcSubStr
|
||||||
|
let rd = c.code[pc].regA
|
||||||
|
regs[ra].strVal = substr(regs[rb].strVal, regs[rc].intVal.int,
|
||||||
|
regs[rd].intVal.int)
|
||||||
|
of opcIndCall, opcIndCallAsgn:
|
||||||
|
# dest = call regStart, n; where regStart = fn, arg1, ...
|
||||||
|
let rb = instr.regB
|
||||||
|
let rc = instr.regC
|
||||||
|
let prc = regs[rb].sym
|
||||||
|
let newPc = compile(c, prc)
|
||||||
|
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
|
||||||
|
newSeq(newFrame.slots, prc.position)
|
||||||
|
if not isEmptyType(prc.typ.sons[0]):
|
||||||
|
newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info)
|
||||||
|
# pass every parameter by var (the language definition allows this):
|
||||||
|
for i in 1 .. rc-1:
|
||||||
|
newFrame.slots[i] = regs[rb+i]
|
||||||
|
# allocate the temporaries:
|
||||||
|
for i in rc .. <prc.position:
|
||||||
|
newFrame.slots[i] = newNode(nkEmpty)
|
||||||
|
tos = newFrame
|
||||||
|
move(regs, newFrame.slots)
|
||||||
|
pc = newPc
|
||||||
|
of opcTJmp:
|
||||||
|
# jump Bx if A != 0
|
||||||
|
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
|
||||||
|
if regs[ra].intVal != 0:
|
||||||
|
inc pc, rbx
|
||||||
|
of opcFJmp:
|
||||||
|
# jump Bx if A == 0
|
||||||
|
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
|
||||||
|
if regs[ra].intVal == 0:
|
||||||
|
inc pc, rbx
|
||||||
|
of opcJmp:
|
||||||
|
# jump Bx
|
||||||
|
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
|
||||||
|
inc pc, rbx
|
||||||
|
of opcBranch:
|
||||||
|
# we know the next instruction is a 'jmp':
|
||||||
|
let branch = c.constants[instr.regBx-wordExcess]
|
||||||
|
var cond = false
|
||||||
|
for j in countup(0, sonsLen(branch) - 2):
|
||||||
|
if overlap(regs[ra], branch.sons[j]):
|
||||||
|
cond = true
|
||||||
|
break
|
||||||
|
assert c.code[pc+1].opcode == opcJmp
|
||||||
|
inc pc
|
||||||
|
# we skip this instruction so that the final 'inc(pc)' skips
|
||||||
|
# the following jump
|
||||||
|
if cond:
|
||||||
|
let instr2 = c.code[pc]
|
||||||
|
let rbx = instr2.regBx - wordExcess - 1 # -1 for the following 'inc pc'
|
||||||
|
inc pc, rbx
|
||||||
|
of opcTry:
|
||||||
|
let rbx = instr.regBx - wordExcess
|
||||||
|
tos.pushSafePoint(pc + rbx)
|
||||||
|
of opcExcept:
|
||||||
|
# just skip it; it's followed by a jump;
|
||||||
|
# we'll execute in the 'raise' handler
|
||||||
|
discard
|
||||||
|
of opcFinally:
|
||||||
|
# just skip it; it's followed by the code we need to execute anyway
|
||||||
|
tos.popSafePoint()
|
||||||
|
of opcFinallyEnd:
|
||||||
|
if c.currentExceptionA != nil:
|
||||||
|
# we are in a cleanup run:
|
||||||
|
pc = cleanupOnException(c, tos, regs)-1
|
||||||
|
if pc < 0:
|
||||||
|
bailOut(c, tos)
|
||||||
|
return
|
||||||
|
of opcRaise:
|
||||||
|
let raised = regs[ra]
|
||||||
|
c.currentExceptionA = raised
|
||||||
|
c.exceptionInstr = pc
|
||||||
|
# -1 because of the following 'inc'
|
||||||
|
pc = cleanupOnException(c, tos, regs) - 1
|
||||||
|
if pc < 0:
|
||||||
|
bailOut(c, tos)
|
||||||
|
return
|
||||||
|
of opcNew:
|
||||||
|
let typ = c.types[instr.regBx - wordExcess]
|
||||||
|
regs[ra] = getNullValue(typ, regs[ra].info)
|
||||||
|
of opcNewSeq:
|
||||||
|
let typ = c.types[instr.regBx - wordExcess]
|
||||||
|
inc pc
|
||||||
|
ensureKind(nkBracket)
|
||||||
|
let instr2 = c.code[pc]
|
||||||
|
let rb = instr2.regA
|
||||||
|
regs[ra].typ = typ
|
||||||
|
newSeq(regs[ra].sons, rb)
|
||||||
|
for i in 0 .. <rb:
|
||||||
|
regs[ra].sons[i] = getNullValue(typ, regs[ra].info)
|
||||||
|
of opcNewStr:
|
||||||
|
decodeB(nkStrLit)
|
||||||
|
regs[ra].strVal = newString(regs[rb].intVal.int)
|
||||||
|
of opcLdImmInt:
|
||||||
|
# dest = immediate value
|
||||||
|
decodeBx(nkIntLit)
|
||||||
|
regs[ra].intVal = rbx
|
||||||
|
of opcLdNull:
|
||||||
|
let typ = c.types[instr.regBx - wordExcess]
|
||||||
|
regs[ra] = getNullValue(typ, c.debug[pc])
|
||||||
|
of opcLdConst:
|
||||||
|
regs[ra] = c.constants.sons[instr.regBx - wordExcess]
|
||||||
|
of opcNBindSym:
|
||||||
|
# trivial implementation:
|
||||||
|
let rb = instr.regB
|
||||||
|
regs[ra] = regs[rb].sons[1]
|
||||||
|
else:
|
||||||
|
InternalError(c.debug[pc], "unknown opcode " & $instr.opcode)
|
||||||
|
inc pc
|
||||||
|
|
||||||
|
proc eval*(c: PCtx, n: PNode): PNode =
|
||||||
|
## eval never returns nil! This simplifies the code a lot and
|
||||||
|
## makes it faster too.
|
||||||
|
let start = genStmt(c, n)
|
||||||
|
# execute new instructions; this redundant opcEof check saves us lots
|
||||||
|
# of allocations in 'execute':
|
||||||
|
if c.code[start].opcode != opcEof:
|
||||||
|
execute(c, start)
|
||||||
|
result = emptyNode
|
||||||
|
|
||||||
|
proc myOpen(module: PSym): PPassContext =
|
||||||
|
#var c = newEvalContext(module, emRepl)
|
||||||
|
#c.features = {allowCast, allowFFI, allowInfiniteLoops}
|
||||||
|
#pushStackFrame(c, newStackFrame())
|
||||||
|
result = newCtx()
|
||||||
|
|
||||||
|
var oldErrorCount: int
|
||||||
|
|
||||||
|
proc myProcess(c: PPassContext, n: PNode): PNode =
|
||||||
|
# don't eval errornous code:
|
||||||
|
if oldErrorCount == msgs.gErrorCounter:
|
||||||
|
result = eval(PCtx(c), n)
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
oldErrorCount = msgs.gErrorCounter
|
||||||
|
|
||||||
|
const vmPass* = makePass(myOpen, nil, myProcess, myProcess)
|
||||||
|
|
||||||
149
compiler/vmdef.nim
Normal file
149
compiler/vmdef.nim
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nimrod Compiler
|
||||||
|
# (c) Copyright 2013 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module contains the type definitions for the new evaluation engine.
|
||||||
|
## An instruction is 1-2 int32s in memory, it is a register based VM.
|
||||||
|
|
||||||
|
import ast, passes, msgs, intsets
|
||||||
|
|
||||||
|
const
|
||||||
|
byteExcess* = 128 # we use excess-K for immediates
|
||||||
|
wordExcess* = 32768
|
||||||
|
|
||||||
|
type
|
||||||
|
TRegister* = range[0..255]
|
||||||
|
TDest* = range[-1 .. 255]
|
||||||
|
TInstr* = distinct uint32
|
||||||
|
|
||||||
|
TInstrFormat = enum
|
||||||
|
ifABC, # three registers
|
||||||
|
ifABx, # A + extended B
|
||||||
|
|
||||||
|
TOpcode* = enum
|
||||||
|
opcEof, # end of code
|
||||||
|
opcRet, # return
|
||||||
|
opcYldYoid, # yield with no value
|
||||||
|
opcYldVal, # yield with a value
|
||||||
|
|
||||||
|
opcAsgnInt,
|
||||||
|
opcAsgnStr,
|
||||||
|
opcAsgnFloat,
|
||||||
|
opcAsgnRef,
|
||||||
|
opcAsgnComplex,
|
||||||
|
|
||||||
|
opcLdArr, # a = b[c]
|
||||||
|
opcWrArr, # a[b] = c
|
||||||
|
opcWrArrRef,
|
||||||
|
opcLdObj, # a = b.c
|
||||||
|
opcWrObj, # a.b = c
|
||||||
|
opcWrObjRef,
|
||||||
|
opcAddr,
|
||||||
|
opcDeref,
|
||||||
|
opcWrStrIdx,
|
||||||
|
|
||||||
|
opcAddInt,
|
||||||
|
opcAddImmInt,
|
||||||
|
opcSubInt,
|
||||||
|
opcSubImmInt,
|
||||||
|
opcLenSeq,
|
||||||
|
opcLenStr,
|
||||||
|
|
||||||
|
opcIncl, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
|
||||||
|
opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
|
||||||
|
opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
|
||||||
|
opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
|
||||||
|
opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcXor,
|
||||||
|
opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
|
||||||
|
opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
|
||||||
|
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
|
||||||
|
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||||
|
opcSwap, opcIsNil, opcOf,
|
||||||
|
opcSubStr, opcConv, opcCast, opcQuit, opcReset,
|
||||||
|
|
||||||
|
opcEcho,
|
||||||
|
opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
|
||||||
|
opcIndCallAsgn, # dest = call regStart, n; where regStart = fn, arg1, ...
|
||||||
|
|
||||||
|
opcRaise,
|
||||||
|
opcNBindSym, # opcodes for the AST manipulation following
|
||||||
|
|
||||||
|
opcTJmp, # jump Bx if A != 0
|
||||||
|
opcFJmp, # jump Bx if A == 0
|
||||||
|
opcJmp, # jump Bx
|
||||||
|
opcBranch, # branch for 'case'
|
||||||
|
opcTry,
|
||||||
|
opcExcept,
|
||||||
|
opcFinally,
|
||||||
|
opcFinallyEnd,
|
||||||
|
opcNew,
|
||||||
|
opcNewSeq,
|
||||||
|
opcNewStr,
|
||||||
|
opcLdNull, # dest = nullvalue(types[Bx])
|
||||||
|
opcLdConst, # dest = constants[Bx]
|
||||||
|
opcLdGlobal, # dest = globals[Bx]
|
||||||
|
opcLdImmInt, # dest = immediate value
|
||||||
|
opcWrGlobal,
|
||||||
|
opcWrGlobalRef
|
||||||
|
|
||||||
|
TBlock* = object
|
||||||
|
label*: PSym
|
||||||
|
fixups*: seq[TPosition]
|
||||||
|
|
||||||
|
TSlotKind* = enum # We try to re-use slots in a smart way to
|
||||||
|
# minimize allocations; however the VM supports arbitrary
|
||||||
|
# temporary slot usage. This is required for the parameter
|
||||||
|
# passing implementation.
|
||||||
|
slotEmpty, # slot is unused
|
||||||
|
slotFixed, # slot is used for a fixed var/param/result
|
||||||
|
slotTempUnknown, # slot but type unknown (argument of proc call)
|
||||||
|
slotTempInt, # some temporary int
|
||||||
|
slotTempFloat, # some temporary float
|
||||||
|
slotTempStr, # some temporary string
|
||||||
|
slotTempComplex # some complex temporary (n.sons field is used)
|
||||||
|
|
||||||
|
PProc* = ref object
|
||||||
|
blocks*: seq[TBlock] # blocks; temp data structure
|
||||||
|
slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
|
||||||
|
maxSlots*: int
|
||||||
|
|
||||||
|
PCtx* = ref TCtx
|
||||||
|
TCtx* = object of passes.TPassContext # code gen context
|
||||||
|
code*: seq[TInstr]
|
||||||
|
debug*: seq[TLineInfo] # line info for every instruction; kept separate
|
||||||
|
# to not slow down interpretation
|
||||||
|
jumpTargets*: TIntSet # we need to mark instructions that are
|
||||||
|
# jump targets;
|
||||||
|
# we must not optimize over a jump target and we
|
||||||
|
# need to generate a label for a jump target when
|
||||||
|
# producing a VM listing
|
||||||
|
globals*: PNode #
|
||||||
|
constants*: PNode # constant data
|
||||||
|
types*: seq[PType] # some instructions reference types (e.g. 'except')
|
||||||
|
currentExceptionA*, currentExceptionB*: PNode
|
||||||
|
exceptionInstr*: int # index of instruction that raised the exception
|
||||||
|
prc*: PProc
|
||||||
|
|
||||||
|
TPosition* = distinct int
|
||||||
|
|
||||||
|
proc newCtx*(): PCtx =
|
||||||
|
PCtx(code: @[], debug: @[], jumpTargets: initIntSet(),
|
||||||
|
globals: newNode(nkStmtList), constants: newNode(nkStmtList), types: @[],
|
||||||
|
prc: PProc(blocks: @[]))
|
||||||
|
|
||||||
|
const
|
||||||
|
firstABxInstr* = opcTJmp
|
||||||
|
largeInstrs* = { # instructions which use 2 int32s instead of 1:
|
||||||
|
opcSubstr, opcConv, opcCast, opcNewSeq, opcOf}
|
||||||
|
slotSomeTemp* = slotTempUnknown
|
||||||
|
|
||||||
|
template opcode*(x: TInstr): TOpcode {.immediate.} = TOpcode(x.uint32 and 0xff'u32)
|
||||||
|
template regA*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 8'u32 and 0xff'u32)
|
||||||
|
template regB*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 16'u32 and 0xff'u32)
|
||||||
|
template regC*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 24'u32)
|
||||||
|
template regBx*(x: TInstr): int {.immediate.} = (x.uint32 shr 16'u32).int
|
||||||
1002
compiler/vmgen.nim
Normal file
1002
compiler/vmgen.nim
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -85,11 +85,13 @@ type
|
||||||
TNimTypeKinds* = set[TNimrodTypeKind]
|
TNimTypeKinds* = set[TNimrodTypeKind]
|
||||||
TNimrodSymKind* = enum
|
TNimrodSymKind* = enum
|
||||||
nskUnknown, nskConditional, nskDynLib, nskParam,
|
nskUnknown, nskConditional, nskDynLib, nskParam,
|
||||||
nskGenericParam, nskTemp, nskType, nskConst,
|
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
|
||||||
nskVar, nskProc, nskMethod, nskIterator,
|
nskConst, nskResult,
|
||||||
|
nskProc, nskMethod, nskIterator,
|
||||||
nskConverter, nskMacro, nskTemplate, nskField,
|
nskConverter, nskMacro, nskTemplate, nskField,
|
||||||
nskEnumField, nskForVar, nskModule, nskLabel,
|
nskEnumField, nskForVar, nskLabel,
|
||||||
nskStub
|
nskStub
|
||||||
|
|
||||||
TNimSymKinds* = set[TNimrodSymKind]
|
TNimSymKinds* = set[TNimrodSymKind]
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -220,6 +222,11 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
|
||||||
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
|
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
|
||||||
## returned even if the symbol is not ambiguous.
|
## returned even if the symbol is not ambiguous.
|
||||||
|
|
||||||
|
proc genSym*(kind: TNimrodSymKind = nskLet; ident = ""): PNimrodNode {.
|
||||||
|
magic: "NGenSym".}
|
||||||
|
## generates a fresh symbol that is guaranteed to be unique. The symbol
|
||||||
|
## needs to occur in a declaration context.
|
||||||
|
|
||||||
proc callsite*(): PNimrodNode {.magic: "NCallSite".}
|
proc callsite*(): PNimrodNode {.magic: "NCallSite".}
|
||||||
## returns the AST if the invokation expression that invoked this macro.
|
## returns the AST if the invokation expression that invoked this macro.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -956,7 +956,7 @@ type
|
||||||
gain*: byte # Whether given states were gained or lost (1/0)
|
gain*: byte # Whether given states were gained or lost (1/0)
|
||||||
state*: byte # A mask of the focus states
|
state*: byte # A mask of the focus states
|
||||||
|
|
||||||
PKeyboardEvent = ptr TKeyboardEvent
|
PKeyboardEvent* = ptr TKeyboardEvent
|
||||||
TKeyboardEvent*{.final.} = object # SDL_KEYDOWN or SDL_KEYUP
|
TKeyboardEvent*{.final.} = object # SDL_KEYDOWN or SDL_KEYUP
|
||||||
# Mouse motion event structure
|
# Mouse motion event structure
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
@ -964,7 +964,7 @@ type
|
||||||
state*: byte # SDL_PRESSED or SDL_RELEASED
|
state*: byte # SDL_PRESSED or SDL_RELEASED
|
||||||
keysym*: TKeySym
|
keysym*: TKeySym
|
||||||
|
|
||||||
PMouseMotionEvent = ptr TMouseMotionEvent
|
PMouseMotionEvent* = ptr TMouseMotionEvent
|
||||||
TMouseMotionEvent*{.final.} = object # SDL_MOUSEMOTION
|
TMouseMotionEvent*{.final.} = object # SDL_MOUSEMOTION
|
||||||
# Mouse button event structure
|
# Mouse button event structure
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
@ -974,7 +974,7 @@ type
|
||||||
xrel*: int16 # The relative motion in the X direction
|
xrel*: int16 # The relative motion in the X direction
|
||||||
yrel*: int16 # The relative motion in the Y direction
|
yrel*: int16 # The relative motion in the Y direction
|
||||||
|
|
||||||
PMouseButtonEvent = ptr TMouseButtonEvent
|
PMouseButtonEvent* = ptr TMouseButtonEvent
|
||||||
TMouseButtonEvent*{.final.} = object # SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP
|
TMouseButtonEvent*{.final.} = object # SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP
|
||||||
# Joystick axis motion event structure
|
# Joystick axis motion event structure
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
@ -984,7 +984,7 @@ type
|
||||||
x*: UInt16 # The X coordinates of the mouse at press time
|
x*: UInt16 # The X coordinates of the mouse at press time
|
||||||
y*: UInt16 # The Y coordinates of the mouse at press time
|
y*: UInt16 # The Y coordinates of the mouse at press time
|
||||||
|
|
||||||
PJoyAxisEvent = ptr TJoyAxisEvent
|
PJoyAxisEvent* = ptr TJoyAxisEvent
|
||||||
TJoyAxisEvent*{.final.} = object # SDL_JOYAXISMOTION
|
TJoyAxisEvent*{.final.} = object # SDL_JOYAXISMOTION
|
||||||
# Joystick trackball motion event structure
|
# Joystick trackball motion event structure
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
@ -992,7 +992,7 @@ type
|
||||||
axis*: byte # The joystick axis index
|
axis*: byte # The joystick axis index
|
||||||
value*: int16 # The axis value (range: -32768 to 32767)
|
value*: int16 # The axis value (range: -32768 to 32767)
|
||||||
|
|
||||||
PJoyBallEvent = ptr TJoyBallEvent
|
PJoyBallEvent* = ptr TJoyBallEvent
|
||||||
TJoyBallEvent*{.final.} = object # SDL_JOYAVBALLMOTION
|
TJoyBallEvent*{.final.} = object # SDL_JOYAVBALLMOTION
|
||||||
# Joystick hat position change event structure
|
# Joystick hat position change event structure
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
@ -1001,7 +1001,7 @@ type
|
||||||
xrel*: int16 # The relative motion in the X direction
|
xrel*: int16 # The relative motion in the X direction
|
||||||
yrel*: int16 # The relative motion in the Y direction
|
yrel*: int16 # The relative motion in the Y direction
|
||||||
|
|
||||||
PJoyHatEvent = ptr TJoyHatEvent
|
PJoyHatEvent* = ptr TJoyHatEvent
|
||||||
TJoyHatEvent*{.final.} = object # SDL_JOYHATMOTION */
|
TJoyHatEvent*{.final.} = object # SDL_JOYHATMOTION */
|
||||||
# Joystick button event structure
|
# Joystick button event structure
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
@ -1013,7 +1013,7 @@ type
|
||||||
# 6 5 4
|
# 6 5 4
|
||||||
# Note that zero means the POV is centered.
|
# Note that zero means the POV is centered.
|
||||||
|
|
||||||
PJoyButtonEvent = ptr TJoyButtonEvent
|
PJoyButtonEvent* = ptr TJoyButtonEvent
|
||||||
TJoyButtonEvent*{.final.} = object # SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP
|
TJoyButtonEvent*{.final.} = object # SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP
|
||||||
# The "window resized" event
|
# The "window resized" event
|
||||||
# When you get this event, you are
|
# When you get this event, you are
|
||||||
|
|
@ -1024,7 +1024,7 @@ type
|
||||||
button*: byte # The joystick button index
|
button*: byte # The joystick button index
|
||||||
state*: byte # SDL_PRESSED or SDL_RELEASED
|
state*: byte # SDL_PRESSED or SDL_RELEASED
|
||||||
|
|
||||||
PResizeEvent = ptr TResizeEvent
|
PResizeEvent* = ptr TResizeEvent
|
||||||
TResizeEvent*{.final.} = object # SDL_VIDEORESIZE
|
TResizeEvent*{.final.} = object # SDL_VIDEORESIZE
|
||||||
# A user-defined event type
|
# A user-defined event type
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
@ -1115,11 +1115,11 @@ type
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
msg*: PSysWMmsg
|
msg*: PSysWMmsg
|
||||||
|
|
||||||
PExposeEvent = ptr TExposeEvent
|
PExposeEvent* = ptr TExposeEvent
|
||||||
TExposeEvent*{.final.} = object
|
TExposeEvent*{.final.} = object
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
||||||
PQuitEvent = ptr TQuitEvent
|
PQuitEvent* = ptr TQuitEvent
|
||||||
TQuitEvent*{.final.} = object
|
TQuitEvent*{.final.} = object
|
||||||
kind*: TEventKind
|
kind*: TEventKind
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,15 @@ while i < s.len:
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
write(stdout, "Du heißt " & s)
|
write(stdout, "Du heißt " & s)
|
||||||
|
|
||||||
|
# bug #544
|
||||||
|
|
||||||
|
type Bar [T; I:range] = array[I, T]
|
||||||
|
proc foo*[T; I:range](a, b: Bar[T, I]): Bar[T, I] =
|
||||||
|
when len(a) != 3:
|
||||||
|
# Error: constant expression expected
|
||||||
|
{.fatal:"Dimensions have to be 3".}
|
||||||
|
#...
|
||||||
|
block:
|
||||||
|
var a, b: Bar[int, 0..2]
|
||||||
|
discard foo(a, b)
|
||||||
|
|
|
||||||
44
tests/manyloc/named_argument_bug/gui.nim
Normal file
44
tests/manyloc/named_argument_bug/gui.nim
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import
|
||||||
|
tri_engine/gfx/gl/primitive,
|
||||||
|
tri_engine/gfx/tex,
|
||||||
|
tri_engine/gfx/color,
|
||||||
|
tri_engine/math/rect,
|
||||||
|
tri_engine/math/vec
|
||||||
|
|
||||||
|
type
|
||||||
|
TWidgetLayer* = enum
|
||||||
|
wlBg = 100,
|
||||||
|
wlOverlap = 200,
|
||||||
|
wlMain = 300,
|
||||||
|
wlOverlay = 400,
|
||||||
|
wlCursor = 500
|
||||||
|
TWidgetLayerType = TWidgetLayer|int
|
||||||
|
TWidgetType* = enum
|
||||||
|
wtImg
|
||||||
|
PWidget* = ref object
|
||||||
|
`type`* : TWidgetType
|
||||||
|
layer* : TWidgetLayer
|
||||||
|
rect* : TRect
|
||||||
|
prim* : PPrimitive
|
||||||
|
|
||||||
|
const
|
||||||
|
baseZ = 5000
|
||||||
|
|
||||||
|
proc newWidget*(`type`: TWidgetType, layer: TWidgetLayerType, rect: TRect): PWidget =
|
||||||
|
new(result)
|
||||||
|
result.`type` = `type`
|
||||||
|
result.layer = layer
|
||||||
|
result.rect = rect
|
||||||
|
|
||||||
|
var verts = newVert(rect)
|
||||||
|
|
||||||
|
# This works because z is accessible at this scope.
|
||||||
|
#var z = baseZ + layer.int
|
||||||
|
#result.prim = newPrimitive(verts, z=z)
|
||||||
|
|
||||||
|
# Doesn't work, because the compiler looks for a symbol called z in this scope,
|
||||||
|
# but it should only check that it is the name of one of the params.
|
||||||
|
#result.prim = newPrimitive(verts, z=baseZ + layer.int)
|
||||||
|
|
||||||
|
# This doesn't work either.
|
||||||
|
result.prim = newPrimitive(verts, z=0)
|
||||||
23
tests/manyloc/named_argument_bug/main.nim
Normal file
23
tests/manyloc/named_argument_bug/main.nim
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import
|
||||||
|
tri_engine/config,
|
||||||
|
tri_engine/math/vec,
|
||||||
|
tri_engine/math/circle,
|
||||||
|
tri_engine/gfx/gl/primitive,
|
||||||
|
tri_engine/gfx/tex,
|
||||||
|
tri_engine/gfx/color,
|
||||||
|
tri_engine/tri_engine,
|
||||||
|
gui
|
||||||
|
|
||||||
|
var isRunning = true
|
||||||
|
|
||||||
|
block:
|
||||||
|
var renderer = newRenderer(w=10, h=10)
|
||||||
|
|
||||||
|
var primitive = newPrimitiveCircle(0.3.TR, color=white(0.5, 0.8), z=15)
|
||||||
|
renderer.addPrimitive(primitive)
|
||||||
|
|
||||||
|
var verts = newVert((min: newV2xy(-0.4), size: newV2xy(0.3)))
|
||||||
|
var primitive2 = newPrimitive(verts, color=red(0.5, 0.8), z=10)
|
||||||
|
renderer.addPrimitive(primitive2)
|
||||||
|
|
||||||
|
var mainMenuWidget = newWidget(wtImg, wlBg, rect=(newV2xy(-1.0), newV2xy(2.0)))
|
||||||
2
tests/manyloc/named_argument_bug/main.nimrod.cfg
Normal file
2
tests/manyloc/named_argument_bug/main.nimrod.cfg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# this file only exists to mark 'main.nim' as the main file
|
||||||
|
|
||||||
6
tests/manyloc/named_argument_bug/tri_engine/config.nim
Normal file
6
tests/manyloc/named_argument_bug/tri_engine/config.nim
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
when defined(doublePrecision):
|
||||||
|
type
|
||||||
|
TR* = float64
|
||||||
|
else:
|
||||||
|
type
|
||||||
|
TR* = float32
|
||||||
57
tests/manyloc/named_argument_bug/tri_engine/gfx/color.nim
Normal file
57
tests/manyloc/named_argument_bug/tri_engine/gfx/color.nim
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import
|
||||||
|
tri_engine/config,
|
||||||
|
tri_engine/math/vec
|
||||||
|
|
||||||
|
from strutils import
|
||||||
|
formatFloat,
|
||||||
|
TFloatFormat,
|
||||||
|
`%`
|
||||||
|
|
||||||
|
from unsigned import
|
||||||
|
`shr`,
|
||||||
|
`and`
|
||||||
|
|
||||||
|
type
|
||||||
|
TColor* = tuple[r, g, b, a: TR]
|
||||||
|
|
||||||
|
converter toColor*(o: uint32): TColor =
|
||||||
|
## Convert an integer to a color. This is mostly useful when the integer is specified as a hex
|
||||||
|
## literal such as 0xFF00007F, which is 100% red, with 50% alpha.
|
||||||
|
## TODO: turn this into a template that can take either 4 or 8 characters?
|
||||||
|
((((o and 0xff000000'u32) shr 24).TR / 255.0).TR,
|
||||||
|
(((o and 0xff0000'u32) shr 16).TR / 255.0).TR,
|
||||||
|
(((o and 0xff00'u32) shr 8).TR / 255.0).TR,
|
||||||
|
(((o and 0xff'u32)).TR / 255.0).TR)
|
||||||
|
|
||||||
|
converter toV4*(o: TColor): TV4[TR] =
|
||||||
|
cast[TV4[TR]](o)
|
||||||
|
|
||||||
|
proc newColor*(r, g, b: TR=0.0, a: TR=1.0): TColor =
|
||||||
|
(r, g, b, a)
|
||||||
|
|
||||||
|
proc white*(rgb, a: TR=1.0): TColor =
|
||||||
|
(rgb, rgb, rgb, a)
|
||||||
|
|
||||||
|
proc red*(r, a: TR=1.0): TColor =
|
||||||
|
newColor(r=r, a=a)
|
||||||
|
|
||||||
|
proc green*(g, a: TR=1.0): TColor =
|
||||||
|
newColor(g=g, a=a)
|
||||||
|
|
||||||
|
proc yellow*(rg, a: TR=1.0): TColor =
|
||||||
|
newColor(r=rg, g=rg, a=a)
|
||||||
|
|
||||||
|
proc blue*(b, a: TR=1.0): TColor =
|
||||||
|
newColor(b=b, a=a)
|
||||||
|
|
||||||
|
proc cyan*(gb, a: TR=1.0): TColor =
|
||||||
|
newColor(g=gb, b=gb, a=a)
|
||||||
|
|
||||||
|
proc purple*(rb, a: TR=1.0): TColor =
|
||||||
|
newColor(r=rb, b=rb, a=a)
|
||||||
|
|
||||||
|
proc `$`*(o: TColor): string =
|
||||||
|
proc f(f: float): string =
|
||||||
|
f.formatFloat(precision=2, format=ffDecimal)
|
||||||
|
|
||||||
|
"(r: $#, g: $#, b: $#, s: $#)" % [f(o.r), f(o.g), f(o.b), f(o.a)]
|
||||||
61
tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim
Normal file
61
tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import
|
||||||
|
opengl,
|
||||||
|
tri_engine/math/vec
|
||||||
|
|
||||||
|
export
|
||||||
|
opengl
|
||||||
|
|
||||||
|
type
|
||||||
|
EGL* = object of E_Base
|
||||||
|
EGL_code* = object of EGL
|
||||||
|
code*: EGL_err
|
||||||
|
EGL_err {.pure.} = enum
|
||||||
|
none = GL_NO_ERROR
|
||||||
|
invalidEnum = GL_INVALID_ENUM
|
||||||
|
invalidVal = GL_INVALID_VALUE
|
||||||
|
invalidOp = GL_INVALID_OPERATION
|
||||||
|
stackOverflow = GL_STACK_OVERFLOW
|
||||||
|
stackUnderflow = GL_STACK_UNDERFLOW
|
||||||
|
outOfMem = GL_OUT_OF_MEMORY
|
||||||
|
invalidFramebufferOp = GL_INVALID_FRAMEBUFFER_OPERATION
|
||||||
|
unknown
|
||||||
|
|
||||||
|
proc newGL_codeException*(msg: string, code: EGL_err): ref EGL_code =
|
||||||
|
result = newException(EGL_code, $code)
|
||||||
|
result.code = code
|
||||||
|
|
||||||
|
proc getErr*(): EGL_err =
|
||||||
|
result = glGetError().EGL_err
|
||||||
|
if result notin {EGL_err.none,
|
||||||
|
EGL_err.invalidEnum,
|
||||||
|
EGL_err.invalidVal,
|
||||||
|
EGL_err.invalidOp,
|
||||||
|
EGL_err.invalidFramebufferOp,
|
||||||
|
EGL_err.outOfMem,
|
||||||
|
EGL_err.stackUnderflow,
|
||||||
|
EGL_err.stackOverflow}:
|
||||||
|
return EGL_err.unknown
|
||||||
|
|
||||||
|
proc errCheck*() =
|
||||||
|
let err = getErr()
|
||||||
|
if err != EGL_err.none:
|
||||||
|
raise newGL_codeException($err, err)
|
||||||
|
|
||||||
|
macro `?`*(call: expr{nkCall}): expr =
|
||||||
|
result = call
|
||||||
|
# Can't yet reference foreign symbols in macros.
|
||||||
|
#errCheck()
|
||||||
|
|
||||||
|
when defined(doublePrecision):
|
||||||
|
const
|
||||||
|
glRealType* = cGLdouble
|
||||||
|
else:
|
||||||
|
const
|
||||||
|
glRealType* = cGLfloat
|
||||||
|
|
||||||
|
proc setUniformV4*[T](loc: GLint, vecs: var openarray[TV4[T]]) =
|
||||||
|
glUniform4fv(loc, vecs.len.GLsizei, cast[PGLfloat](vecs[0].addr))
|
||||||
|
|
||||||
|
proc setUniformV4*[T](loc: GLint, vec: TV4[T]) =
|
||||||
|
var vecs = [vec]
|
||||||
|
setUniformV4(loc, vecs)
|
||||||
157
tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim
Normal file
157
tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
import
|
||||||
|
math,
|
||||||
|
tri_engine/config,
|
||||||
|
tri_engine/gfx/gl/gl,
|
||||||
|
tri_engine/gfx/tex,
|
||||||
|
tri_engine/gfx/color,
|
||||||
|
tri_engine/math/vec,
|
||||||
|
tri_engine/math/rect,
|
||||||
|
tri_engine/math/circle
|
||||||
|
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
type
|
||||||
|
TVert* = tuple[pos: TV2[TR], texCoord: TV2[TR]]
|
||||||
|
TVertAttrib* = object
|
||||||
|
i* : GLuint
|
||||||
|
size* : GLint
|
||||||
|
stride* : GLsizei
|
||||||
|
offset* : PGLvoid
|
||||||
|
TVertMode* = enum
|
||||||
|
vmTriStrip = GLtriangleStrip,
|
||||||
|
vmTriFan = GLtriangleFan
|
||||||
|
TZ_range* = range[-100_000..100_000]
|
||||||
|
PPrimitive* = ref object
|
||||||
|
verts* : seq[TVert]
|
||||||
|
indices* : seq[GLushort]
|
||||||
|
arrBufId* : GLuint
|
||||||
|
elemArrBufId* : GLuint
|
||||||
|
tex* : TTex
|
||||||
|
color* : TColor
|
||||||
|
vertMode* : TVertMode
|
||||||
|
z* : int
|
||||||
|
|
||||||
|
proc newVert*(pos, texCoord: TV2): TVert =
|
||||||
|
(pos, texCoord)
|
||||||
|
|
||||||
|
proc newVertQuad*(min, minRight, maxLeft, max: TV2[TR]): seq[TVert] =
|
||||||
|
@[newVert(min, newV2()),
|
||||||
|
newVert(minRight, newV2(x=1.0)),
|
||||||
|
newVert(maxLeft, newV2(y=1.0)),
|
||||||
|
newVert(max, newV2xy(1.0))
|
||||||
|
]
|
||||||
|
|
||||||
|
proc newVert*(rect: rect.TRect): seq[TVert] =
|
||||||
|
newVertQuad(rect.min, newV2(rect.max.x, rect.min.y), newV2(rect.min.x, rect.max.y), rect.max)
|
||||||
|
|
||||||
|
proc newVertAttrib(i: GLuint, size: GLint, stride: GLsizei, offset: PGLvoid): TVertAttrib =
|
||||||
|
TVertAttrib(i: i, size: size, stride: stride, offset: offset)
|
||||||
|
|
||||||
|
proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openarray[T]): GLuint =
|
||||||
|
result = 0.GLuint
|
||||||
|
?glGenBuffers(1, result.addr)
|
||||||
|
?glBindBuffer(vboTarget, result)
|
||||||
|
|
||||||
|
let size = (data.len * T.sizeof).GLsizeiptr
|
||||||
|
?glBufferData(vboTarget, size, data[0].addr, objUsage)
|
||||||
|
|
||||||
|
proc newPrimitive*(verts: var seq[TVert],
|
||||||
|
vertMode=vmTriStrip,
|
||||||
|
tex=whiteTex(),
|
||||||
|
color=white(),
|
||||||
|
z: TZ_range=0): PPrimitive =
|
||||||
|
var indices = newSeq[GLushort](verts.len)
|
||||||
|
for i in 0 .. <verts.len:
|
||||||
|
indices[i] = i.GLushort
|
||||||
|
|
||||||
|
new(result)
|
||||||
|
result.verts = verts
|
||||||
|
result.indices = indices
|
||||||
|
|
||||||
|
result.arrBufId = genBuf(GLarrayBuffer, GL_STATIC_DRAW, verts)
|
||||||
|
result.elemArrBufId = genBuf(GLelementArrayBuffer, GL_STATIC_DRAW, indices)
|
||||||
|
result.tex = tex
|
||||||
|
result.color = color
|
||||||
|
result.vertMode = vertMode
|
||||||
|
result.z = z
|
||||||
|
|
||||||
|
proc bindBufs*(o: PPrimitive) =
|
||||||
|
?glBindBuffer(GLarrayBuffer, o.arrBufId)
|
||||||
|
?glBindBuffer(GLelementArrayBuffer, o.elemArrBufId)
|
||||||
|
|
||||||
|
proc enableVertAttribArrs*() =
|
||||||
|
?glEnableVertexAttribArray(0)
|
||||||
|
?glEnableVertexAttribArray(1)
|
||||||
|
|
||||||
|
proc disableVertAttribArrs*() =
|
||||||
|
?glDisableVertexAttribArray(1)
|
||||||
|
?glDisableVertexAttribArray(0)
|
||||||
|
|
||||||
|
proc setVertAttribPointers*() =
|
||||||
|
let vertSize {.global.} = TVert.sizeof.GLint
|
||||||
|
?glVertexAttribPointer(0, 2, glRealType, false, vertSize, nil)
|
||||||
|
?glVertexAttribPointer(1, 2, glRealType, false, vertSize, cast[PGLvoid](TR.sizeof * 2))
|
||||||
|
|
||||||
|
proc updVerts*(o: PPrimitive, start, `end`: int, f: proc(i: int, vert: var TVert)) =
|
||||||
|
assert start <= `end`
|
||||||
|
assert `end` < o.verts.len
|
||||||
|
for i in start..`end`:
|
||||||
|
f(i, o.verts[i])
|
||||||
|
|
||||||
|
?glBindBuffer(GLarrayBuffer, o.arrBufId)
|
||||||
|
|
||||||
|
let byteLen = `end` - start + 1 * TVert.sizeof
|
||||||
|
let byteOffset = start * TVert.sizeof
|
||||||
|
?glBufferSubData(GLarrayBuffer,
|
||||||
|
byteOffset.GLintptr, # Offset. Is this right?
|
||||||
|
byteLen.GLsizeiptr, # Size.
|
||||||
|
cast[PGLvoid](cast[int](o.verts[0].addr) + byteOffset))
|
||||||
|
|
||||||
|
proc updAllVerts(o: PPrimitive, f: proc(i: int, vert: var TVert)) =
|
||||||
|
for i in 0 .. <o.verts.len:
|
||||||
|
f(i, o.verts[i])
|
||||||
|
|
||||||
|
?glBindBuffer(GLarrayBuffer, o.arrBufId)
|
||||||
|
|
||||||
|
# Discard old buffer before creating a new one.
|
||||||
|
let byteLen = (o.verts.len * TVert.sizeof).GLsizeiptr
|
||||||
|
?glBufferData(GLarrayBuffer, byteLen, nil, GLstaticDraw)
|
||||||
|
?glBufferData(GLarrayBuffer, byteLen, o.verts[0].addr, GLstaticDraw)
|
||||||
|
|
||||||
|
proc newVertCircle*(circle: TCircle, nSegs: Natural=0): seq[TVert] =
|
||||||
|
let nSegs = if nSegs == 0:
|
||||||
|
(circle.r.sqrt() * 400.0).int # TODO: Base this on the window resolution?
|
||||||
|
else:
|
||||||
|
max(nSegs, 3)
|
||||||
|
|
||||||
|
let theta: TR = (PI * 2.0) / (nSegs.TR)
|
||||||
|
let tanFactor = theta.tan()
|
||||||
|
let radialFactor = theta.cos()
|
||||||
|
var x = circle.r
|
||||||
|
var y: TR = 0.0
|
||||||
|
|
||||||
|
result = newSeq[TVert](nSegs)
|
||||||
|
#result[0] = newVert(circle.p, newV2xy(0.5))
|
||||||
|
for i in 1 .. <nSegs:
|
||||||
|
let pos = newV2(x + circle.p.x, y + circle.p.y)
|
||||||
|
let texCoord = pos * newV2xy(1.0 / circle.r)
|
||||||
|
|
||||||
|
result[i] = newVert(pos, texCoord)
|
||||||
|
|
||||||
|
let tx = -y
|
||||||
|
let ty = x
|
||||||
|
x += tx * tanFactor
|
||||||
|
y += ty * tanFactor
|
||||||
|
|
||||||
|
x *= radialFactor
|
||||||
|
y *= radialFactor
|
||||||
|
|
||||||
|
result.add(result[1])
|
||||||
|
|
||||||
|
proc newPrimitiveCircle*(circle: TCircle,
|
||||||
|
nSegs: Natural=0,
|
||||||
|
tex=whiteTex(),
|
||||||
|
color=white(),
|
||||||
|
z: TZ_range=0): PPrimitive =
|
||||||
|
var verts = newVertCircle(circle, nSegs)
|
||||||
|
newPrimitive(verts, vmTriFan, tex, color, z)
|
||||||
103
tests/manyloc/named_argument_bug/tri_engine/gfx/gl/shader.nim
Normal file
103
tests/manyloc/named_argument_bug/tri_engine/gfx/gl/shader.nim
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
import
|
||||||
|
pure/os,
|
||||||
|
tri_engine/gfx/gl/gl
|
||||||
|
|
||||||
|
type
|
||||||
|
TShader* = object
|
||||||
|
id*: GL_handle
|
||||||
|
TShaderType* {.pure.} = enum
|
||||||
|
frag = GL_FRAGMENT_SHADER,
|
||||||
|
vert = GL_VERTEX_SHADER
|
||||||
|
E_Shader* = object of E_Base
|
||||||
|
E_UnknownShaderType* = object of E_Shader
|
||||||
|
|
||||||
|
converter pathToShaderType*(s: string): TShaderType =
|
||||||
|
case s.splitFile().ext:
|
||||||
|
of ".vs":
|
||||||
|
return TShaderType.vert
|
||||||
|
of ".fs":
|
||||||
|
return TShaderType.frag
|
||||||
|
else:
|
||||||
|
raise newException(E_UnknownShaderType, "Can't determine shader type from file extension: " & s)
|
||||||
|
|
||||||
|
proc setSrc*(shader: TShader, src: string) =
|
||||||
|
var s = src.cstring
|
||||||
|
?glShaderSource(shader.id, 1, cast[cstringarray](s.addr), nil)
|
||||||
|
|
||||||
|
proc newShader*(id: GL_handle): TShader =
|
||||||
|
if id != 0 and not (?glIsShader(id)).bool:
|
||||||
|
raise newException(E_GL, "Invalid shader ID: " & $id)
|
||||||
|
|
||||||
|
result.id = id
|
||||||
|
|
||||||
|
proc shaderInfoLog*(o: TShader): string =
|
||||||
|
var log {.global.}: array[0..1024, char]
|
||||||
|
var logLen: GLsizei
|
||||||
|
?glGetShaderInfoLog(o.id, log.len.GLsizei, logLen, cast[PGLchar](log.addr))
|
||||||
|
cast[string](log.addr).substr(0, logLen)
|
||||||
|
|
||||||
|
proc compile*(shader: TShader, path="") =
|
||||||
|
?glCompileShader(shader.id)
|
||||||
|
var compileStatus = 0.GLint
|
||||||
|
?glGetShaderIv(shader.id, GL_COMPILE_STATUS, compileStatus.addr)
|
||||||
|
|
||||||
|
if compileStatus == 0:
|
||||||
|
raise newException(E_GL, if path.len == 0:
|
||||||
|
shaderInfoLog(shader)
|
||||||
|
else:
|
||||||
|
path & ":\n" & shaderInfoLog(shader)
|
||||||
|
)
|
||||||
|
|
||||||
|
proc newShaderFromSrc*(src: string, `type`: TShaderType): TShader =
|
||||||
|
result.id = ?glCreateShader(`type`.GLenum)
|
||||||
|
result.setSrc(src)
|
||||||
|
result.compile()
|
||||||
|
|
||||||
|
proc newShaderFromFile*(path: string): TShader =
|
||||||
|
newShaderFromSrc(readFile(path), path)
|
||||||
|
|
||||||
|
type
|
||||||
|
TProgram* = object
|
||||||
|
id*: GL_handle
|
||||||
|
shaders: seq[TShader]
|
||||||
|
|
||||||
|
proc attach*(o: TProgram, shader: TShader) =
|
||||||
|
?glAttachShader(o.id, shader.id)
|
||||||
|
|
||||||
|
proc infoLog*(o: TProgram): string =
|
||||||
|
var log {.global.}: array[0..1024, char]
|
||||||
|
var logLen: GLsizei
|
||||||
|
?glGetProgramInfoLog(o.id, log.len.GLsizei, logLen, cast[PGLchar](log.addr))
|
||||||
|
cast[string](log.addr).substr(0, logLen)
|
||||||
|
|
||||||
|
proc link*(o: TProgram) =
|
||||||
|
?glLinkProgram(o.id)
|
||||||
|
var linkStatus = 0.GLint
|
||||||
|
?glGetProgramIv(o.id, GL_LINK_STATUS, linkStatus.addr)
|
||||||
|
if linkStatus == 0:
|
||||||
|
raise newException(E_GL, o.infoLog())
|
||||||
|
|
||||||
|
proc validate*(o: TProgram) =
|
||||||
|
?glValidateProgram(o.id)
|
||||||
|
var validateStatus = 0.GLint
|
||||||
|
?glGetProgramIv(o.id, GL_VALIDATE_STATUS, validateStatus.addr)
|
||||||
|
if validateStatus == 0:
|
||||||
|
raise newException(E_GL, o.infoLog())
|
||||||
|
|
||||||
|
proc newProgram*(shaders: seq[TShader]): TProgram =
|
||||||
|
result.id = ?glCreateProgram()
|
||||||
|
if result.id == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
for shader in shaders:
|
||||||
|
if shader.id == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
?result.attach(shader)
|
||||||
|
|
||||||
|
result.shaders = shaders
|
||||||
|
result.link()
|
||||||
|
result.validate()
|
||||||
|
|
||||||
|
proc use*(o: TProgram) =
|
||||||
|
?glUseProgram(o.id)
|
||||||
31
tests/manyloc/named_argument_bug/tri_engine/gfx/tex.nim
Normal file
31
tests/manyloc/named_argument_bug/tri_engine/gfx/tex.nim
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import
|
||||||
|
tri_engine/gfx/gl/gl
|
||||||
|
|
||||||
|
type
|
||||||
|
TTex* = object
|
||||||
|
id*: GLuint
|
||||||
|
|
||||||
|
var gWhiteTex = TTex(id: 0)
|
||||||
|
|
||||||
|
proc setTexParams() =
|
||||||
|
?glTexParameteri(GLtexture2D, GLtextureMinFilter, GLlinear)
|
||||||
|
|
||||||
|
#glTexParameteri(GLtexture2D, GLtextureMagFilter, GLlinear)
|
||||||
|
?glTexParameteri(GLtexture2D, GLTextureMagFilter, GLnearest)
|
||||||
|
|
||||||
|
?glTexParameteri(GLtexture2D, GLTextureWrapS, GLClampToEdge)
|
||||||
|
?glTexParameteri(GLtexture2D, GLTextureWrapT, GLClampToEdge)
|
||||||
|
|
||||||
|
proc whiteTex*(): TTex =
|
||||||
|
if gWhiteTex.id.int != 0:
|
||||||
|
return gWhiteTex
|
||||||
|
|
||||||
|
?glGenTextures(1, gWhiteTex.id.addr)
|
||||||
|
?glBindTexture(GLtexture2D, gWhiteTex.id)
|
||||||
|
setTexParams()
|
||||||
|
|
||||||
|
var pixel = [255'u8, 255'u8, 255'u8, 255'u8]
|
||||||
|
?glTexImage2D(GLtexture2D, 0, GL_RGBA, 1, 1, 0, GL_BGRA, cGLUnsignedByte, pixel[0].addr)
|
||||||
|
?glBindTexture(GLtexture2D, 0)
|
||||||
|
|
||||||
|
result = gWhiteTex
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import
|
||||||
|
tri_engine/config,
|
||||||
|
tri_engine/math/vec
|
||||||
|
|
||||||
|
type
|
||||||
|
TCircle* = tuple[p: TV2[TR], r: TR]
|
||||||
|
|
||||||
|
converter toCircle*(o: TR): TCircle =
|
||||||
|
(newV2(), o)
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import
|
||||||
|
tri_engine/config,
|
||||||
|
tri_engine/math/vec
|
||||||
|
|
||||||
|
type
|
||||||
|
TRect* = tuple[min, size: TV2[TR]]
|
||||||
|
|
||||||
|
proc max*(o: TRect): TV2[TR] = o.min + o.size
|
||||||
55
tests/manyloc/named_argument_bug/tri_engine/math/vec.nim
Normal file
55
tests/manyloc/named_argument_bug/tri_engine/math/vec.nim
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import
|
||||||
|
macros,
|
||||||
|
tri_engine/config
|
||||||
|
|
||||||
|
type
|
||||||
|
TV2*[T:TNumber=TR] = array[0..1, T]
|
||||||
|
TV3*[T:TNumber=TR] = array[0..2, T]
|
||||||
|
TV4*[T:TNumber=TR] = array[0..3, T]
|
||||||
|
TVT*[T:TNumber=TR] = TV2|TV3|TV4
|
||||||
|
#TV2* = array[0..1, TR]
|
||||||
|
#TV3* = array[0..2, TR]
|
||||||
|
#TV4* = array[0..3, TR]
|
||||||
|
|
||||||
|
# TODO: Change to TVT when compiler issue is resolved.
|
||||||
|
proc `$`*[T](o: TV2[T]): string =
|
||||||
|
result = "("
|
||||||
|
for i in 0 .. <o.len:
|
||||||
|
result &= $o[0]
|
||||||
|
if i != o.len - 1:
|
||||||
|
result &= ", "
|
||||||
|
|
||||||
|
result & ")"
|
||||||
|
|
||||||
|
proc newV2T*[T](x, y: T=0): TV2[T] =
|
||||||
|
[x, y]
|
||||||
|
|
||||||
|
proc newV2*(x, y: TR=0.0): TV2[TR] =
|
||||||
|
[x, y]
|
||||||
|
|
||||||
|
proc newV2xy*(xy: TR): TV2[TR] =
|
||||||
|
[xy, xy]
|
||||||
|
|
||||||
|
proc x*[T](o: TV2[T]): T =
|
||||||
|
o[0]
|
||||||
|
|
||||||
|
proc y*[T](o: TV2[T]): T =
|
||||||
|
o[1]
|
||||||
|
|
||||||
|
proc `*`*(lhs: TV2[TR], rhs: TV2[TR]): TV2[TR] =
|
||||||
|
[(lhs.x * rhs.x).TR, (lhs.y * rhs.y).TR]
|
||||||
|
|
||||||
|
proc `+`*(lhs: TV2[TR], rhs: TV2[TR]): TV2[TR] =
|
||||||
|
[(lhs.x + rhs.x).TR, (lhs.y + rhs.y).TR]
|
||||||
|
|
||||||
|
#proc dotProduct[T](a: TVT[T], b: TVT[T]): T =
|
||||||
|
# for i in 0 .. a.len - 1:
|
||||||
|
# result += a[i] * b[i]
|
||||||
|
|
||||||
|
proc dot[T](a, b: TV2[T]): T =
|
||||||
|
for i in 0 .. <a.len:
|
||||||
|
result += a[i] * b[i]
|
||||||
|
|
||||||
|
assert dot(newV2(), newV2()) == 0.0
|
||||||
|
assert dot(newV2(2, 3), newV2(6, 7)) == 33.0
|
||||||
|
assert dot([2.0, 3.0], [6.0, 7.0]) == 33.0
|
||||||
106
tests/manyloc/named_argument_bug/tri_engine/tri_engine.nim
Normal file
106
tests/manyloc/named_argument_bug/tri_engine/tri_engine.nim
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import
|
||||||
|
algorithm,
|
||||||
|
tri_engine/config,
|
||||||
|
tri_engine/gfx/gl/gl,
|
||||||
|
tri_engine/gfx/gl/primitive,
|
||||||
|
tri_engine/gfx/gl/shader,
|
||||||
|
tri_engine/gfx/color
|
||||||
|
|
||||||
|
const primitiveVs = """
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 pos;
|
||||||
|
layout(location = 1) in vec2 texCoord;
|
||||||
|
|
||||||
|
out vec2 uv;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = vec4(pos, 0, 1);
|
||||||
|
uv = texCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
const primitiveFs = """
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 uv;
|
||||||
|
out vec4 color;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
uniform vec4 inColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
color = texture(tex, uv) * inColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
var gW, gH: Natural = 0
|
||||||
|
|
||||||
|
proc w*(): int =
|
||||||
|
gW
|
||||||
|
|
||||||
|
proc h*(): int =
|
||||||
|
gH
|
||||||
|
|
||||||
|
type
|
||||||
|
PRenderer = ref object
|
||||||
|
primitiveProgram: TProgram
|
||||||
|
primitives: seq[PPrimitive]
|
||||||
|
|
||||||
|
proc setupGL() =
|
||||||
|
?glEnable(GLblend)
|
||||||
|
?glBlendFunc(GLsrcAlpha, GLoneMinusSrcAlpha)
|
||||||
|
?glClearColor(0.0, 0.0, 0.0, 1.0)
|
||||||
|
?glPolygonMode(GLfrontAndBack, GLfill)
|
||||||
|
|
||||||
|
proc newRenderer*(w, h: Positive): PRenderer =
|
||||||
|
gW = w
|
||||||
|
gH = h
|
||||||
|
|
||||||
|
new(result)
|
||||||
|
newSeq(result.primitives, 0)
|
||||||
|
loadExtensions()
|
||||||
|
setupGL()
|
||||||
|
result.primitiveProgram = newProgram(@[
|
||||||
|
newShaderFromSrc(primitiveVs, TShaderType.vert),
|
||||||
|
newShaderFromSrc(primitiveFs, TShaderType.frag)])
|
||||||
|
|
||||||
|
proc draw(o: PRenderer, p: PPrimitive) =
|
||||||
|
let loc = proc(x: string): Glint =
|
||||||
|
result = glGetUniformLocation(o.primitiveProgram.id, x)
|
||||||
|
if result == -1:
|
||||||
|
raise newException(E_GL, "Shader error: " & x & " does not correspond to a uniform variable")
|
||||||
|
|
||||||
|
setUniformV4(loc("inColor"), p.color)
|
||||||
|
?glActiveTexture(GLtexture0)
|
||||||
|
?glBindTexture(GLtexture2D, p.tex.id.GLuint)
|
||||||
|
?glUniform1i(loc("tex"), 0)
|
||||||
|
|
||||||
|
p.bindBufs()
|
||||||
|
setVertAttribPointers()
|
||||||
|
|
||||||
|
?glDrawElements(p.vertMode.GLenum, p.indices.len.GLsizei, cGLunsignedShort, nil)
|
||||||
|
|
||||||
|
proc draw*(o: PRenderer) =
|
||||||
|
?glClear(GLcolorBufferBit)
|
||||||
|
o.primitiveProgram.use()
|
||||||
|
|
||||||
|
enableVertAttribArrs()
|
||||||
|
var zSortedPrimitives = o.primitives
|
||||||
|
zSortedPrimitives.sort(proc(x, y: PPrimitive): int =
|
||||||
|
if x.z < y.z:
|
||||||
|
-1
|
||||||
|
elif x.z > y.z:
|
||||||
|
1
|
||||||
|
else:
|
||||||
|
0)
|
||||||
|
|
||||||
|
for x in zSortedPrimitives:
|
||||||
|
o.draw(x)
|
||||||
|
|
||||||
|
disableVertAttribArrs()
|
||||||
|
|
||||||
|
proc addPrimitive*(o: PRenderer, p: PPrimitive) =
|
||||||
|
o.primitives.add(p)
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -8,7 +8,6 @@ version 0.9.4
|
||||||
- mocking support with ``tyProxy`` that does: fallback for ``.`` operator
|
- mocking support with ``tyProxy`` that does: fallback for ``.`` operator
|
||||||
- overloading of ``.``? Special case ``.=``?
|
- overloading of ``.``? Special case ``.=``?
|
||||||
- built-in 'getImpl'
|
- built-in 'getImpl'
|
||||||
- macros.gensym still missing?
|
|
||||||
- optimize 'genericReset'; 'newException' leads to code bloat
|
- optimize 'genericReset'; 'newException' leads to code bloat
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ Bugfixes
|
||||||
Library Additions
|
Library Additions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Added ``macros.genSym`` builtin for AST generation.
|
||||||
|
|
||||||
|
|
||||||
Changes affecting backwards compatibility
|
Changes affecting backwards compatibility
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue