Merge branch 'master' into asyncmacro

This commit is contained in:
Dominik Picheta 2013-07-25 19:33:08 +01:00
commit 3e7ea859ec
28 changed files with 2485 additions and 19 deletions

View file

@ -488,7 +488,7 @@ type
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
mNBindSym, mLocals, mNCallSite,
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:
const

View file

@ -1249,6 +1249,16 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
of mNBindSym:
# trivial implementation:
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:
result = evalAux(c, n.sons[1], {})
if isSpecial(result): return

33
compiler/nimeval.nim Normal file
View 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)

View file

@ -625,6 +625,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
result = newIntNodeT(sonsLen(a), n)
else:
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:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
of mConStrStr:

View file

@ -312,7 +312,7 @@ proc semGenericStmt(c: PContext, n: PNode,
n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
closeScope(c)
of nkPragma, nkPragmaExpr: nil
of nkExprColonExpr:
of nkExprColonExpr, nkExprEqExpr:
checkMinSonsLen(n, 2)
result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
else:

View file

@ -841,7 +841,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
LocalError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
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
if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
checkSonsLen(n, 3)

View file

@ -959,8 +959,8 @@ proc skipGenericAlias*(t: PType): PType =
proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool =
for i in countup(0, typeClass.sonsLen - 1):
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:
case req.kind
of tyGenericBody:

584
compiler/vm.nim Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

View file

@ -85,11 +85,13 @@ type
TNimTypeKinds* = set[TNimrodTypeKind]
TNimrodSymKind* = enum
nskUnknown, nskConditional, nskDynLib, nskParam,
nskGenericParam, nskTemp, nskType, nskConst,
nskVar, nskProc, nskMethod, nskIterator,
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
nskConst, nskResult,
nskProc, nskMethod, nskIterator,
nskConverter, nskMacro, nskTemplate, nskField,
nskEnumField, nskForVar, nskModule, nskLabel,
nskEnumField, nskForVar, nskLabel,
nskStub
TNimSymKinds* = set[TNimrodSymKind]
type
@ -220,6 +222,11 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
## 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".}
## returns the AST if the invokation expression that invoked this macro.

View file

@ -956,7 +956,7 @@ type
gain*: byte # Whether given states were gained or lost (1/0)
state*: byte # A mask of the focus states
PKeyboardEvent = ptr TKeyboardEvent
PKeyboardEvent* = ptr TKeyboardEvent
TKeyboardEvent*{.final.} = object # SDL_KEYDOWN or SDL_KEYUP
# Mouse motion event structure
kind*: TEventKind
@ -964,7 +964,7 @@ type
state*: byte # SDL_PRESSED or SDL_RELEASED
keysym*: TKeySym
PMouseMotionEvent = ptr TMouseMotionEvent
PMouseMotionEvent* = ptr TMouseMotionEvent
TMouseMotionEvent*{.final.} = object # SDL_MOUSEMOTION
# Mouse button event structure
kind*: TEventKind
@ -974,7 +974,7 @@ type
xrel*: int16 # The relative motion in the X direction
yrel*: int16 # The relative motion in the Y direction
PMouseButtonEvent = ptr TMouseButtonEvent
PMouseButtonEvent* = ptr TMouseButtonEvent
TMouseButtonEvent*{.final.} = object # SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP
# Joystick axis motion event structure
kind*: TEventKind
@ -984,7 +984,7 @@ type
x*: UInt16 # The X 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
# Joystick trackball motion event structure
kind*: TEventKind
@ -992,7 +992,7 @@ type
axis*: byte # The joystick axis index
value*: int16 # The axis value (range: -32768 to 32767)
PJoyBallEvent = ptr TJoyBallEvent
PJoyBallEvent* = ptr TJoyBallEvent
TJoyBallEvent*{.final.} = object # SDL_JOYAVBALLMOTION
# Joystick hat position change event structure
kind*: TEventKind
@ -1001,7 +1001,7 @@ type
xrel*: int16 # The relative motion in the X direction
yrel*: int16 # The relative motion in the Y direction
PJoyHatEvent = ptr TJoyHatEvent
PJoyHatEvent* = ptr TJoyHatEvent
TJoyHatEvent*{.final.} = object # SDL_JOYHATMOTION */
# Joystick button event structure
kind*: TEventKind
@ -1013,7 +1013,7 @@ type
# 6 5 4
# Note that zero means the POV is centered.
PJoyButtonEvent = ptr TJoyButtonEvent
PJoyButtonEvent* = ptr TJoyButtonEvent
TJoyButtonEvent*{.final.} = object # SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP
# The "window resized" event
# When you get this event, you are
@ -1024,7 +1024,7 @@ type
button*: byte # The joystick button index
state*: byte # SDL_PRESSED or SDL_RELEASED
PResizeEvent = ptr TResizeEvent
PResizeEvent* = ptr TResizeEvent
TResizeEvent*{.final.} = object # SDL_VIDEORESIZE
# A user-defined event type
kind*: TEventKind
@ -1115,11 +1115,11 @@ type
kind*: TEventKind
msg*: PSysWMmsg
PExposeEvent = ptr TExposeEvent
PExposeEvent* = ptr TExposeEvent
TExposeEvent*{.final.} = object
kind*: TEventKind
PQuitEvent = ptr TQuitEvent
PQuitEvent* = ptr TQuitEvent
TQuitEvent*{.final.} = object
kind*: TEventKind

View file

@ -46,3 +46,15 @@ while i < s.len:
i = i + 1
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)

View 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)

View 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)))

View file

@ -0,0 +1,2 @@
# this file only exists to mark 'main.nim' as the main file

View file

@ -0,0 +1,6 @@
when defined(doublePrecision):
type
TR* = float64
else:
type
TR* = float32

View 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)]

View 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)

View 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)

View 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)

View 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

View file

@ -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)

View file

@ -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

View 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

View 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)

View file

@ -8,7 +8,6 @@ version 0.9.4
- mocking support with ``tyProxy`` that does: fallback for ``.`` operator
- overloading of ``.``? Special case ``.=``?
- built-in 'getImpl'
- macros.gensym still missing?
- optimize 'genericReset'; 'newException' leads to code bloat

View file

@ -14,6 +14,8 @@ Bugfixes
Library Additions
-----------------
- Added ``macros.genSym`` builtin for AST generation.
Changes affecting backwards compatibility
-----------------------------------------