Merge branch 'master' of github.com:Araq/Nimrod

This commit is contained in:
Araq 2012-10-03 20:51:22 +02:00
commit c2b8669e04
25 changed files with 827 additions and 497 deletions

View file

@ -350,6 +350,8 @@ type
# pass of semProcTypeNode performed after instantiation. # pass of semProcTypeNode performed after instantiation.
# this won't be needed if we don't perform this redundant # this won't be needed if we don't perform this redundant
# second pass (stay tuned). # second pass (stay tuned).
tfRetType # marks return types in proc (used to detect type classes
# used as return types for return type inference)
tfAll, # type class requires all constraints to be met (default) tfAll, # type class requires all constraints to be met (default)
tfAny, # type class requires any constraint to be met tfAny, # type class requires any constraint to be met
tfCapturesEnv, # whether proc really captures some environment tfCapturesEnv, # whether proc really captures some environment
@ -779,6 +781,11 @@ proc add*(father, son: PNode) =
proc `[]`*(n: PNode, i: int): PNode {.inline.} = proc `[]`*(n: PNode, i: int): PNode {.inline.} =
result = n.sons[i] result = n.sons[i]
# son access operators with support for negative indices
template `{}`*(n: PNode, i: int): expr = n[i -| n]
template `{}=`*(n: PNode, i: int, s: PNode): stmt =
n.sons[i -| n] = s
var emptyNode* = newNode(nkEmpty) var emptyNode* = newNode(nkEmpty)
# There is a single empty node that is shared! Do not overwrite it! # There is a single empty node that is shared! Do not overwrite it!

View file

@ -41,6 +41,7 @@ type
callsite: PNode # for 'callsite' magic callsite: PNode # for 'callsite' magic
mode*: TEvalMode mode*: TEvalMode
globals*: TIdNodeTable # state of global vars globals*: TIdNodeTable # state of global vars
getType*: proc(n: PNode): PNode
PEvalContext* = ref TEvalContext PEvalContext* = ref TEvalContext
@ -521,7 +522,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
else: result = nil else: result = nil
if result == nil or {sfImportc, sfForward} * s.flags != {}: if result == nil or {sfImportc, sfForward} * s.flags != {}:
result = raiseCannotEval(c, n.info) result = raiseCannotEval(c, n.info)
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
result = evalAux(c, n.sons[1], {efLValue}) result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return if isSpecial(result): return
@ -875,6 +876,27 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode =
else: else:
internalAssert false internalAssert false
proc evalIsOp*(n: PNode): PNode =
InternalAssert n.sonsLen == 3 and
n[1].kind == nkSym and n[1].sym.kind == skType and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = n[1].sym.typ
if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize
of "closure":
let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure))
else:
let t2 = n[2].typ
var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1)
else: sameType(t1, t2)
result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ
proc expectString(n: PNode) = proc expectString(n: PNode) =
if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
GlobalError(n.info, errStringLiteralExpected) GlobalError(n.info, errStringLiteralExpected)
@ -968,6 +990,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
of mTypeTrait: of mTypeTrait:
n.sons[1] = evalAux(c, n.sons[1], {}) n.sons[1] = evalAux(c, n.sons[1], {})
result = evalTypeTrait(n, c.module) result = evalTypeTrait(n, c.module)
of mIs:
n.sons[1] = evalAux(c, n.sons[1], {})
result = evalIsOp(n)
of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module) of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
of mStaticExec: of mStaticExec:
let cmd = evalAux(c, n.sons[1], {}) let cmd = evalAux(c, n.sons[1], {})
@ -1067,7 +1092,10 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {}) result = evalAux(c, n.sons[1], {})
if isSpecial(result): return if isSpecial(result): return
if result.kind != nkIdent: stackTrace(c, n, errFieldXNotFound, "ident") if result.kind != nkIdent: stackTrace(c, n, errFieldXNotFound, "ident")
of mNGetType: result = evalAux(c, n.sons[1], {}) of mNGetType:
var ast = evalAux(c, n.sons[1], {})
InternalAssert c.getType != nil
result = c.getType(ast)
of mNStrVal: of mNStrVal:
result = evalAux(c, n.sons[1], {}) result = evalAux(c, n.sons[1], {})
if isSpecial(result): return if isSpecial(result): return
@ -1128,7 +1156,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
var a = result var a = result
result = evalAux(c, n.sons[2], {efLValue}) result = evalAux(c, n.sons[2], {efLValue})
if isSpecial(result): return if isSpecial(result): return
a.typ = result.typ # XXX: exception handling? InternalAssert result.kind == nkSym and result.sym.kind == skType
a.typ = result.sym.typ
result = emptyNode result = emptyNode
of mNSetStrVal: of mNSetStrVal:
result = evalAux(c, n.sons[1], {efLValue}) result = evalAux(c, n.sons[1], {efLValue})

View file

@ -575,19 +575,22 @@ proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
type type
TErrorHandling = enum doNothing, doAbort, doRaise TErrorHandling = enum doNothing, doAbort, doRaise
proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
if msg == errInternal: template maybeTrace =
assert(false) # we want a stack trace here if defined(debug) or gVerbosity >= 3:
writeStackTrace()
if msg == errInternal:
writeStackTrace() # we always want a stack trace here
if msg >= fatalMin and msg <= fatalMax: if msg >= fatalMin and msg <= fatalMax:
if gVerbosity >= 3: assert(false) maybeTrace()
quit(1) quit(1)
if msg >= errMin and msg <= errMax: if msg >= errMin and msg <= errMax:
if gVerbosity >= 3: assert(false) maybeTrace()
inc(gErrorCounter) inc(gErrorCounter)
options.gExitcode = 1'i8 options.gExitcode = 1'i8
if gErrorCounter >= gErrorMax or eh == doAbort: if gErrorCounter >= gErrorMax or eh == doAbort:
if gVerbosity >= 3: assert(false) quit(1) # one error stops the compiler
quit(1) # one error stops the compiler
elif eh == doRaise: elif eh == doRaise:
raiseRecoverableError(s) raiseRecoverableError(s)

View file

@ -743,7 +743,7 @@ proc isExprStart(p: TParser): bool =
case p.tok.tokType case p.tok.tokType
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind, of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
tkTuple, tkType, tkWhen: tkTuple, tkType, tkWhen, tkCase:
result = true result = true
else: result = false else: result = false
@ -763,9 +763,9 @@ proc parseExpr(p: var TParser): PNode =
case p.tok.tokType: case p.tok.tokType:
of tkIf: result = parseIfExpr(p, nkIfExpr) of tkIf: result = parseIfExpr(p, nkIfExpr)
of tkWhen: result = parseIfExpr(p, nkWhenExpr) of tkWhen: result = parseIfExpr(p, nkWhenExpr)
of tkCase: result = parseCase(p)
else: result = lowestExpr(p) else: result = lowestExpr(p)
# XXX needs proper support: # XXX needs proper support:
#of tkCase: result = parseCase(p)
#of tkTry: result = parseTry(p) #of tkTry: result = parseTry(p)
proc primary(p: var TParser, skipSuffix = false): PNode = proc primary(p: var TParser, skipSuffix = false): PNode =
@ -1044,9 +1044,9 @@ proc parseCase(p: var TParser): PNode =
if b.kind == nkElse: break if b.kind == nkElse: break
if wasIndented: if wasIndented:
eat(p, tkDed) if p.tok.tokType != tkEof: eat(p, tkDed)
popInd(p.lex) popInd(p.lex)
proc parseTry(p: var TParser): PNode = proc parseTry(p: var TParser): PNode =
result = newNodeP(nkTryStmt, p) result = newNodeP(nkTryStmt, p)
getTok(p) getTok(p)

View file

@ -43,6 +43,7 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind)
proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind)
proc addResultNode(c: PContext, n: PNode) proc addResultNode(c: PContext, n: PNode)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc typeMismatch(n: PNode, formal, actual: PType) = proc typeMismatch(n: PNode, formal, actual: PType) =
if formal.kind != tyError and actual.kind != tyError: if formal.kind != tyError and actual.kind != tyError:
@ -156,8 +157,18 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
markUsed(n, sym) markUsed(n, sym)
if sym == c.p.owner: if sym == c.p.owner:
GlobalError(n.info, errRecursiveDependencyX, sym.name.s) GlobalError(n.info, errRecursiveDependencyX, sym.name.s)
if c.evalContext == nil: if c.evalContext == nil:
c.evalContext = newEvalContext(c.module, "", emStatic) c.evalContext = newEvalContext(c.module, "", emStatic)
c.evalContext.getType = proc (n: PNode): PNode =
var e = tryExpr(c, n)
if e == nil:
result = symNodeFromType(c, errorType(c), n.info)
elif e.typ == nil:
result = newSymNode(getSysSym"void")
else:
result = symNodeFromType(c, e.typ, n.info)
result = evalMacroCall(c.evalContext, n, nOrig, sym) result = evalMacroCall(c.evalContext, n, nOrig, sym)
if semCheck: result = semAfterMacroCall(c, result, sym) if semCheck: result = semAfterMacroCall(c, result, sym)

View file

@ -281,18 +281,29 @@ proc semOf(c: PContext, n: PNode): PNode =
n.typ = getSysType(tyBool) n.typ = getSysType(tyBool)
result = n result = n
proc semIs(c: PContext, n: PNode): PNode = proc semIs(c: PContext, n: PNode): PNode =
if sonsLen(n) == 3: if sonsLen(n) != 3:
n.typ = getSysType(tyBool)
let a = semTypeNode(c, n[1], nil)
n.sons[1] = newNodeIT(nkType, n[1].info, a)
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let b = semTypeNode(c, n[2], nil)
n.sons[2] = newNodeIT(nkType, n[2].info, b)
else:
LocalError(n.info, errXExpectsTwoArguments, "is") LocalError(n.info, errXExpectsTwoArguments, "is")
result = n
result = n
n.typ = getSysType(tyBool)
n.sons[1] = semExprWithType(c, n[1])
if n[1].typ.kind != tyTypeDesc:
LocalError(n[0].info, errTypeExpected)
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let t2 = semTypeNode(c, n[2], nil)
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
if n[1].typ.sonsLen == 0:
# this is a typedesc variable, leave for evals
return
else:
let t1 = n[1].typ.sons[0]
# BUGFIX: don't evaluate this too early: ``T is void``
if not containsGenericType(t1): result = evalIsOp(n)
proc semOpAux(c: PContext, n: PNode, tailToExclude = 1) = proc semOpAux(c: PContext, n: PNode, tailToExclude = 1) =
for i in countup(1, sonsLen(n) - tailToExclude): for i in countup(1, sonsLen(n) - tailToExclude):
var a = n.sons[i] var a = n.sons[i]
@ -1048,8 +1059,20 @@ proc semAsgn(c: PContext, n: PNode): PNode =
localError(a.info, errXCannotBeAssignedTo, localError(a.info, errXCannotBeAssignedTo,
renderTree(a, {renderNoComments})) renderTree(a, {renderNoComments}))
else: else:
n.sons[1] = semExprWithType(c, n.sons[1]) var
n.sons[1] = fitNode(c, le, n.sons[1]) rhs = semExprWithType(c, n.sons[1])
lhs = n.sons[0]
if lhs.kind == nkSym and lhs.sym.kind == skResult and
lhs.sym.typ.kind == tyGenericParam:
if matchTypeClass(lhs.typ, rhs.typ):
InternalAssert c.p.resultSym != nil
lhs.typ = rhs.typ
c.p.resultSym.typ = rhs.typ
c.p.owner.typ.sons[0] = rhs.typ
else:
typeMismatch(n, lhs.typ, rhs.typ)
n.sons[1] = fitNode(c, le, rhs)
fixAbstractType(c, n) fixAbstractType(c, n)
asgnToResultVar(c, n, n.sons[0], n.sons[1]) asgnToResultVar(c, n, n.sons[0], n.sons[1])
result = n result = n
@ -1214,12 +1237,7 @@ proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
else: else:
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# we replace this node by a 'true' or 'false' node:
if sonsLen(n) != 2: return semDirectOp(c, n, flags)
result = newIntNode(nkIntLit, 0)
result.info = n.info
result.typ = getSysType(tyBool)
# watch out, hacks ahead: # watch out, hacks ahead:
let oldErrorCount = msgs.gErrorCounter let oldErrorCount = msgs.gErrorCounter
let oldErrorMax = msgs.gErrorMax let oldErrorMax = msgs.gErrorMax
@ -1241,8 +1259,8 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
let oldProcCon = c.p let oldProcCon = c.p
c.generics = newGenericsCache() c.generics = newGenericsCache()
try: try:
discard semExpr(c, n.sons[1]) result = semExpr(c, n, flags)
result.intVal = ord(msgs.gErrorCounter == oldErrorCount) if msgs.gErrorCounter != oldErrorCount: result = nil
except ERecoverableError: except ERecoverableError:
nil nil
# undo symbol table changes (as far as it's possible): # undo symbol table changes (as far as it's possible):
@ -1259,6 +1277,14 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
msgs.gErrorCounter = oldErrorCount msgs.gErrorCounter = oldErrorCount
msgs.gErrorMax = oldErrorMax msgs.gErrorMax = oldErrorMax
proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we replace this node by a 'true' or 'false' node:
if sonsLen(n) != 2: return semDirectOp(c, n, flags)
result = newIntNode(nkIntLit, ord(tryExpr(c, n, flags) != nil))
result.info = n.info
result.typ = getSysType(tyBool)
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) == 3: if sonsLen(n) == 3:
# XXX ugh this is really a hack: shallowCopy() can be overloaded only # XXX ugh this is really a hack: shallowCopy() can be overloaded only
@ -1352,19 +1378,28 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
m = fitNode(c, typ, n.sons[i]) m = fitNode(c, typ, n.sons[i])
addSon(result, m) addSon(result, m)
proc semTableConstr(c: PContext, n: PNode): PNode = proc semTableConstr(c: PContext, n: PNode): PNode =
# we simply transform ``{key: value, key2: value}`` to # we simply transform ``{key: value, key2, key3: value}`` to
# ``[(key, value), (key2, value2)]`` # ``[(key, value), (key2, value2), (key3, value2)]``
result = newNodeI(nkBracket, n.info) result = newNodeI(nkBracket, n.info)
var lastKey = 0
for i in 0..n.len-1: for i in 0..n.len-1:
var x = n.sons[i] var x = n.sons[i]
if x.kind == nkExprColonExpr and sonsLen(x) == 2: if x.kind == nkExprColonExpr and sonsLen(x) == 2:
for j in countup(lastKey, i-1):
var pair = newNodeI(nkPar, x.info)
pair.add(n.sons[j])
pair.add(x[1])
result.add(pair)
var pair = newNodeI(nkPar, x.info) var pair = newNodeI(nkPar, x.info)
pair.add(x[0]) pair.add(x[0])
pair.add(x[1]) pair.add(x[1])
result.add(pair) result.add(pair)
else:
illFormedAst(x) lastKey = i+1
if lastKey != n.len: illFormedAst(n)
result = semExpr(c, result) result = semExpr(c, result)
type type
@ -1507,6 +1542,57 @@ proc semMacroStmt(c: PContext, n: PNode, flags: TExprFlags,
renderTree(a, {renderNoComments})) renderTree(a, {renderNoComments}))
result = errorNode(c, n) result = errorNode(c, n)
proc semCaseExpr(c: PContext, caseStmt: PNode): PNode =
# The case expression is simply rewritten to a StmtListExpr:
# var res {.noInit, genSym.}: type(values)
#
# case E
# of X: res = value1
# of Y: res = value2
#
# res
var
info = caseStmt.info
resVar = newSym(skVar, getIdent":res", getCurrOwner(), info)
resNode = newSymNode(resVar, info)
resType: PType
resVar.flags = { sfGenSym, sfNoInit }
for i in countup(1, caseStmt.len - 1):
var cs = caseStmt[i]
case cs.kind
of nkOfBranch, nkElifBranch, nkElse:
# the value is always the last son regardless of the branch kind
cs.checkMinSonsLen 1
var value = cs{-1}
if value.kind == nkStmtList: value.kind = nkStmtListExpr
value = semExprWithType(c, value)
if resType == nil:
resType = value.typ
elif not sameType(resType, value.typ):
# XXX: semeType is a bit too harsh.
# work on finding a common base type.
# this will be useful for arrays/seq too:
# [ref DerivedA, ref DerivedB, ref Base]
typeMismatch(cs, resType, value.typ)
cs{-1} = newNode(nkAsgn, cs.info, @[resNode, value])
else:
IllFormedAst(caseStmt)
result = newNode(nkStmtListExpr, info, @[
newNode(nkVarSection, info, @[
newNode(nkIdentDefs, info, @[
resNode,
symNodeFromType(c, resType, info),
emptyNode])]),
caseStmt,
resNode])
result = semStmtListExpr(c, result)
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n result = n
if gCmd == cmdIdeTools: suggestExpr(c, n) if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -1686,7 +1772,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkTryStmt: result = semTry(c, n) of nkTryStmt: result = semTry(c, n)
of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n) of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
of nkForStmt, nkParForStmt: result = semFor(c, n) of nkForStmt, nkParForStmt: result = semFor(c, n)
of nkCaseStmt: result = semCase(c, n) of nkCaseStmt:
if efWantStmt in flags: result = semCase(c, n)
else: result = semCaseExpr(c, n)
of nkReturnStmt: result = semReturn(c, n) of nkReturnStmt: result = semReturn(c, n)
of nkAsmStmt: result = semAsm(c, n) of nkAsmStmt: result = semAsm(c, n)
of nkYieldStmt: result = semYield(c, n) of nkYieldStmt: result = semYield(c, n)

View file

@ -610,17 +610,6 @@ 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 mIs:
# BUGFIX: don't evaluate this too early: ``T is void``
if not containsGenericType(n[1].typ):
if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize
of "closure":
let t = skipTypes(n[1].typ, abstractRange)
result = newIntNodeT(ord(t.kind == tyProc and
t.callConv == ccClosure), n)
elif not containsGenericType(n[2].typ):
result = newIntNodeT(ord(sameType(n[1].typ, n[2].typ)), n)
of mAstToStr: of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
of mConStrStr: of mConStrStr:

View file

@ -261,16 +261,16 @@ proc semGenericStmt(c: PContext, n: PNode,
else: else:
a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, toBind) a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, toBind)
of nkEnumTy: of nkEnumTy:
checkMinSonsLen(n, 1) if n.sonsLen > 0:
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, toBind) n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, toBind)
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
var a: PNode var a: PNode
case n.sons[i].kind case n.sons[i].kind
of nkEnumFieldDef: a = n.sons[i].sons[0] of nkEnumFieldDef: a = n.sons[i].sons[0]
of nkIdent: a = n.sons[i] of nkIdent: a = n.sons[i]
else: illFormedAst(n) else: illFormedAst(n)
addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1) addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1)
of nkObjectTy, nkTupleTy: of nkObjectTy, nkTupleTy:
nil nil
of nkFormalParams: of nkFormalParams:
@ -306,6 +306,9 @@ proc semGenericStmt(c: PContext, n: PNode,
n.sons[bodyPos] = semGenericStmtScope(c, body, flags, toBind) n.sons[bodyPos] = semGenericStmtScope(c, body, flags, toBind)
closeScope(c.tab) closeScope(c.tab)
of nkPragma, nkPragmaExpr: nil of nkPragma, nkPragmaExpr: nil
of nkExprColonExpr:
checkMinSonsLen(n, 2)
result.sons[1] = semGenericStmt(c, n.sons[1], flags, toBind)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semGenericStmt(c, n.sons[i], flags, toBind) result.sons[i] = semGenericStmt(c, n.sons[i], flags, toBind)

View file

@ -25,8 +25,13 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
s.flags = s.flags + {sfUsed, sfFromGeneric} s.flags = s.flags + {sfUsed, sfFromGeneric}
var t = PType(IdTableGet(pt, q.typ)) var t = PType(IdTableGet(pt, q.typ))
if t == nil: if t == nil:
LocalError(a.info, errCannotInstantiateX, s.name.s) if tfRetType in q.typ.flags:
t = errorType(c) # keep the generic type and allow the return type to be bound
# later by semAsgn in return type inference scenario
t = q.typ
else:
LocalError(a.info, errCannotInstantiateX, s.name.s)
t = errorType(c)
elif t.kind == tyGenericParam: elif t.kind == tyGenericParam:
InternalError(a.info, "instantiateGenericParamList: " & q.name.s) InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
elif t.kind == tyGenericInvokation: elif t.kind == tyGenericInvokation:
@ -163,7 +168,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.typ = newTypeS(tyProc, c) result.typ = newTypeS(tyProc, c)
rawAddSon(result.typ, nil) rawAddSon(result.typ, nil)
result.typ.callConv = fn.typ.callConv result.typ.callConv = fn.typ.callConv
ParamsTypeCheck(c, result.typ)
var oldPrc = GenericCacheGet(c, entry) var oldPrc = GenericCacheGet(c, entry)
if oldPrc == nil: if oldPrc == nil:
c.generics.generics.add(entry) c.generics.generics.add(entry)
@ -174,6 +178,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
if fn.kind != skTemplate: if fn.kind != skTemplate:
instantiateBody(c, n, result) instantiateBody(c, n, result)
sideEffectsCheck(c, result) sideEffectsCheck(c, result)
ParamsTypeCheck(c, result.typ)
else: else:
result = oldPrc result = oldPrc
popInfoContext() popInfoContext()

View file

@ -584,22 +584,20 @@ proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
result.typ = newTypeS(tyTypeDesc, c) result.typ = newTypeS(tyTypeDesc, c)
result.typ.sons = paramType.sons result.typ.sons = paramType.sons
of tyDistinct: of tyDistinct:
# type T1 = distinct expr
# type S1 = distinct Sortable
# proc x(a, b: T1, c, d: S1)
# This forces bindOnce behavior for the type class, equivalent to
# proc x[T, S](a, b: T, c, d: S)
result = paramTypeClass(c, paramType.lastSon, procKind) result = paramTypeClass(c, paramType.lastSon, procKind)
result.id = paramType.sym.name # disable the bindOnce behavior for the type class
result.id = nil
return
of tyGenericBody: of tyGenericBody:
# type Foo[T] = object # type Foo[T] = object
# proc x(a: Foo, b: Foo) # proc x(a: Foo, b: Foo)
result.typ = newTypeS(tyTypeClass, c) result.typ = newTypeS(tyTypeClass, c)
result.typ.addSonSkipIntLit(paramType) result.typ.addSonSkipIntLit(paramType)
result.id = paramType.sym.name # bindOnce by default
of tyTypeClass: of tyTypeClass:
result.typ = copyType(paramType, getCurrOwner(), false) result.typ = copyType(paramType, getCurrOwner(), false)
else: nil else: nil
# bindOnce by default
if paramType.sym != nil: result.id = paramType.sym.name
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType: PType, paramName: string, paramType: PType, paramName: string,
@ -619,7 +617,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
let s = SymtabGet(c.tab, paramTypId) let s = SymtabGet(c.tab, paramTypId)
# tests/run/tinterf triggers this: # tests/run/tinterf triggers this:
if s != nil: result = s.typ if s != nil: result = s.typ
else: else:
LocalError(info, errCannotInstantiateX, paramName) LocalError(info, errCannotInstantiateX, paramName)
result = errorType(c) result = errorType(c)
else: else:
@ -684,8 +682,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
for j in countup(0, length-3): for j in countup(0, length-3):
var arg = newSymG(skParam, a.sons[j], c) var arg = newSymG(skParam, a.sons[j], c)
var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s, var finalType = liftParamType(c, kind, genericParams, typ,
arg.info).skipIntLit arg.name.s, arg.info).skipIntLit
arg.typ = finalType arg.typ = finalType
arg.position = counter arg.position = counter
inc(counter) inc(counter)
@ -703,6 +701,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if skipTypes(r, {tyGenericInst}).kind != tyEmpty: if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
if r.sym == nil or sfAnon notin r.sym.flags: if r.sym == nil or sfAnon notin r.sym.flags:
r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info)
r.flags.incl tfRetType
result.sons[0] = skipIntLit(r) result.sons[0] = skipIntLit(r)
res.typ = result.sons[0] res.typ = result.sons[0]
@ -800,22 +799,25 @@ 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:
let op = n.sons[0].ident if n[0].kind == nkIdent:
if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": let op = n.sons[0].ident
var if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
t1 = semTypeNode(c, n.sons[1], nil) var
t2 = semTypeNode(c, n.sons[2], nil) t1 = semTypeNode(c, n.sons[1], nil)
if t1 == nil: t2 = semTypeNode(c, n.sons[2], nil)
LocalError(n.sons[1].info, errTypeExpected) if t1 == nil:
result = newOrPrevType(tyError, prev, c) LocalError(n.sons[1].info, errTypeExpected)
elif t2 == nil: result = newOrPrevType(tyError, prev, c)
LocalError(n.sons[2].info, errTypeExpected) elif t2 == nil:
result = newOrPrevType(tyError, prev, c) LocalError(n.sons[2].info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
else:
result = newTypeS(tyTypeClass, c)
result.addSonSkipIntLit(t1)
result.addSonSkipIntLit(t2)
result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
else: else:
result = newTypeS(tyTypeClass, c) result = semTypeExpr(c, n)
result.addSonSkipIntLit(t1)
result.addSonSkipIntLit(t2)
result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
else: else:
result = semTypeExpr(c, n) result = semTypeExpr(c, n)
of nkCurlyExpr: of nkCurlyExpr:

View file

@ -59,6 +59,7 @@ type
proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
result = copyNode(n) result = copyNode(n)
@ -66,7 +67,7 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
for i in 0 .. safeLen(n)-1: for i in 0 .. safeLen(n)-1:
# XXX HACK: ``f(a, b)``, avoid to instantiate `f` # XXX HACK: ``f(a, b)``, avoid to instantiate `f`
if i == 0: result.add(n[i]) if i == 0: result.add(n[i])
else: result.add(prepareNode(cl, n[i])) else: result.add(ReplaceTypeVarsN(cl, n[i]))
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
if n == nil: return if n == nil: return

View file

@ -11,7 +11,7 @@
## the call to overloaded procs, generic procs and operators. ## the call to overloaded procs, generic procs and operators.
import import
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
magicsys, condsyms, idents, lexer, options magicsys, condsyms, idents, lexer, options
type type
@ -257,32 +257,6 @@ proc tupleRel(c: var TCandidate, f, a: PType): TTypeRelation =
var y = a.n.sons[i].sym var y = a.n.sons[i].sym
if x.name.id != y.name.id: return isNone if x.name.id != y.name.id: return isNone
proc matchTypeClass(c: var TCandidate, typeClass, t: PType): TTypeRelation =
for i in countup(0, typeClass.sonsLen - 1):
let req = typeClass.sons[i]
var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind
if not match:
case req.kind
of tyGenericBody:
if t.kind == tyGenericInst and t.sons[0] == req:
match = true
put(c.bindings, typeClass, t)
of tyTypeClass:
match = matchTypeClass(c, req, t) == isGeneric
else: nil
elif t.kind in {tyObject}:
match = sameType(t, req)
if tfAny in typeClass.flags:
if match: return isGeneric
else:
if not match: return isNone
# if the loop finished without returning, either all constraints matched
# or none of them matched.
result = if tfAny in typeClass.flags: isNone else: isGeneric
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
@ -325,6 +299,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isNone result = isNone
else: nil else: nil
proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =
result = if matchTypeClass(c.bindings, f, a): isGeneric
else: isNone
proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
# is a subtype of f? # is a subtype of f?
result = isNone result = isNone

View file

@ -626,9 +626,9 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
var c = initSameTypeClosure() var c = initSameTypeClosure()
c.flags = flags c.flags = flags
result = SameTypeAux(a, b, c) result = SameTypeAux(a, b, c)
proc equalParam(a, b: PSym): TParamsEquality = proc equalParam(a, b: PSym): TParamsEquality =
if SameTypeOrNil(a.typ, b.typ): if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}):
if a.ast == b.ast: if a.ast == b.ast:
result = paramsEqual result = paramsEqual
elif a.ast != nil and b.ast != nil: elif a.ast != nil and b.ast != nil:
@ -904,7 +904,38 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
if i >= a.sonslen or a.sons[i] == nil: return false if i >= a.sonslen or a.sons[i] == nil: return false
a = a.sons[i] a = a.sons[i]
result = a.kind == last result = a.kind == last
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
if not match:
case req.kind
of tyGenericBody:
if t.kind == tyGenericInst and t.sons[0] == req:
match = true
IdTablePut(bindings, typeClass, t)
of tyTypeClass:
match = matchTypeClass(bindings, req, t)
else: nil
elif t.kind in {tyObject}:
match = sameType(t, req)
if tfAny in typeClass.flags:
if match: return true
else:
if not match: return false
# if the loop finished without returning, either all constraints matched
# or none of them matched.
result = if tfAny in typeClass.flags: false else: true
proc matchTypeClass*(typeClass, typ: PType): bool =
var bindings: TIdTable
initIdTable(bindings)
result = matchTypeClass(bindings, typeClass, typ)
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
assert(kind in {skVar, skLet, skConst, skParam, skResult}) assert(kind in {skVar, skLet, skConst, skParam, skResult})
# if we have already checked the type, return true, because we stop the # if we have already checked the type, return true, because we stop the

View file

@ -2237,6 +2237,28 @@ An if expression always results in a value, so the ``else`` part is
required. ``Elif`` parts are also allowed (but unlikely to be good required. ``Elif`` parts are also allowed (but unlikely to be good
style). style).
When expression
~~~~~~~~~~~~~~~
Just like an `if expression`, but corresponding to the when statement.
Case expression
~~~~~~~~~~~~~~~
The `case expression` is again very similar to the case statement:
.. code-block:: nimrod
var favoriteFood = case animal
of "dog": "bones"
of "cat": "mice"
elif animal.endsWith"whale": "plankton"
else:
echo "I'm not sure what to serve, but everybody loves ice cream"
"ice cream"
As seen in the above example, the case expression can also introduce side
effects. When multiple statements are given for a branch, Nimrod will use
the last expression as the result value, much like in an `expr` template.
Table constructor Table constructor
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -2244,10 +2266,10 @@ Table constructor
A `table constructor`:idx: is syntactic sugar for an array constructor: A `table constructor`:idx: is syntactic sugar for an array constructor:
.. code-block:: nimrod .. code-block:: nimrod
{"key1": "value1", "key2": "value2"} {"key1": "value1", "key2", "key3": "value2"}
# is the same as: # is the same as:
[("key1", "value1"), ("key2", "value2")] [("key1", "value1"), ("key2", "value2"), ("key3", "value")]
The empty table can be written ``{:}`` (in contrast to the empty set The empty table can be written ``{:}`` (in contrast to the empty set
@ -2940,6 +2962,13 @@ from the proc body. This is usually used with the ``auto`` type class:
.. code-block:: nimrod .. code-block:: nimrod
proc makePair(a, b): auto = (first: a, second: b) proc makePair(a, b): auto = (first: a, second: b)
The return type will be treated as additional generic param and can be
explicitly specified at call sites as any other generic param.
Future versions of nimrod may also support overloading based on the return type
of the overloads. In such settings, the expected result type at call sites may
also influence the inferred return type.
Symbol lookup in generics Symbol lookup in generics
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
@ -3431,7 +3460,7 @@ typedesc
`typedesc` is a special type allowing you to treat types as compile-time values `typedesc` is a special type allowing you to treat types as compile-time values
(i.e. if types are compile-time values and all values have a type, then (i.e. if types are compile-time values and all values have a type, then
typedesc must be their type). typedesc must be their type).
When used as a regular proc param, typedesc acts as a type class. The proc When used as a regular proc param, typedesc acts as a type class. The proc
will be instantiated for each unique type parameter and you can refer to the will be instantiated for each unique type parameter and you can refer to the
@ -3457,15 +3486,14 @@ a type-safe wrapper for the unsafe `printf` function form C:
macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr = macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr =
var i = 0 var i = 0
for c in formatChars(formatString): for c in formatChars(formatString):
const FormatChars = { var expectedType = case c
'c': char, of 'c': char
'd', 'i', 'x', 'X': int, of 'd', 'i', 'x', 'X': int
'f', 'e', 'E', 'g', 'G': float, of 'f', 'e', 'E', 'g', 'G': float
's': string, of 's': string
'p': pointer, of 'p': pointer
} else: EOutOfRange
var expectedType = find(FormatChars, c, EOutOfRange)
var actualType = args[i].getType var actualType = args[i].getType
inc i inc i
@ -3642,15 +3670,20 @@ proc with no side effects:
destructor pragma destructor pragma
----------------- -----------------
`RAII`:idx:
`automatic variables`:idx:
`destructors`:idx:
The `destructor` pragma is used to mark a proc to act as a type destructor. The `destructor` pragma is used to mark a proc to act as a type destructor.
The proc must have a single parameter, having a concrete type. The proc must have a single parameter with a concrete type (the name of a
generic type is allowed too).
Destructors will be automatically invoked when a local stack variable goes Destructors will be automatically invoked when a local stack variable goes
out of scope. If a record type features a field with destructable type and out of scope.
If a record type features a field with destructable type and
the user have not provided explicit implementation, Nimrod will automatically the user have not provided explicit implementation, Nimrod will automatically
generate a destructor for the record type. generate a destructor for the record type. Nimrod will automatically insert
calls to any base class destructors in both user-defined and generated
destructors.
procvar pragma procvar pragma
-------------- --------------
@ -3658,7 +3691,6 @@ The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
procedural variable. procedural variable.
compileTime pragma compileTime pragma
------------------ ------------------
The `compileTime`:idx: pragma is used to mark a proc to be used at compile The `compileTime`:idx: pragma is used to mark a proc to be used at compile

View file

@ -1,194 +1,188 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module contains the interface to the compiler's abstract syntax ## This module contains the interface to the compiler's abstract syntax
## tree (`AST`:idx:). Macros operate on this tree. ## tree (`AST`:idx:). Macros operate on this tree.
## .. include:: ../doc/astspec.txt ## .. include:: ../doc/astspec.txt
type type
TNimrodNodeKind* = enum TNimrodNodeKind* = enum
nnkNone, nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkEmpty, nnkIdent, nnkSym,
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit, nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit, nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit, nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit,
nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit, nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit,
nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall, nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall,
nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr, nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr,
nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix, nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix,
nnkPrefix, nnkPostfix, nnkPar, nnkCurly, nnkCurlyExpr, nnkPrefix, nnkPostfix, nnkPar, nnkCurly, nnkCurlyExpr,
nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange, nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr, nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted, nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted,
nnkTableConstr, nnkBind, nnkTableConstr, nnkBind,
nnkClosedSymChoice, nnkClosedSymChoice,
nnkOpenSymChoice, nnkOpenSymChoice,
nnkHiddenStdConv, nnkHiddenStdConv,
nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast, nnkStaticExpr, nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast, nnkStaticExpr,
nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
nnkStringToCString, nnkCStringToString, nnkAsgn, nnkStringToCString, nnkCStringToString, nnkAsgn,
nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit,
nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef, nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef,
nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt, nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt,
nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt, nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt,
nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection, nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
nnkConstDef, nnkTypeDef, nnkConstDef, nnkTypeDef,
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
nnkIncludeStmt, nnkBindStmt, nnkMixinStmt, nnkIncludeStmt, nnkBindStmt, nnkMixinStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkRefTy, nnkPtrTy, nnkVarTy,
nnkConstTy, nnkMutableTy, nnkConstTy, nnkMutableTy,
nnkDistinctTy, nnkDistinctTy,
nnkProcTy, nnkEnumTy, nnkProcTy, nnkEnumTy,
nnkEnumFieldDef, nnkEnumFieldDef,
nnkArglist, nnkPattern nnkArglist, nnkPattern
nnkReturnToken nnkReturnToken
TNimNodeKinds* = set[TNimrodNodeKind] TNimNodeKinds* = set[TNimrodNodeKind]
TNimrodTypeKind* = enum TNimrodTypeKind* = enum
ntyNone, ntyBool, ntyChar, ntyEmpty, ntyNone, ntyBool, ntyChar, ntyEmpty,
ntyArrayConstr, ntyNil, ntyExpr, ntyStmt, ntyArrayConstr, ntyNil, ntyExpr, ntyStmt,
ntyTypeDesc, ntyGenericInvokation, ntyGenericBody, ntyGenericInst, ntyTypeDesc, ntyGenericInvokation, ntyGenericBody, ntyGenericInst,
ntyGenericParam, ntyDistinct, ntyEnum, ntyOrdinal, ntyGenericParam, ntyDistinct, ntyEnum, ntyOrdinal,
ntyArray, ntyObject, ntyTuple, ntySet, ntyArray, ntyObject, ntyTuple, ntySet,
ntyRange, ntyPtr, ntyRef, ntyVar, ntyRange, ntyPtr, ntyRef, ntyVar,
ntySequence, ntyProc, ntyPointer, ntyOpenArray, ntySequence, ntyProc, ntyPointer, ntyOpenArray,
ntyString, ntyCString, ntyForward, ntyInt, ntyString, ntyCString, ntyForward, ntyInt,
ntyInt8, ntyInt16, ntyInt32, ntyInt64, ntyInt8, ntyInt16, ntyInt32, ntyInt64,
ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128 ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128
TNimTypeKinds* = set[TNimrodTypeKind] TNimTypeKinds* = set[TNimrodTypeKind]
TNimrodSymKind* = enum TNimrodSymKind* = enum
nskUnknown, nskConditional, nskDynLib, nskParam, nskUnknown, nskConditional, nskDynLib, nskParam,
nskGenericParam, nskTemp, nskType, nskConst, nskGenericParam, nskTemp, nskType, nskConst,
nskVar, nskProc, nskMethod, nskIterator, nskVar, nskProc, nskMethod, nskIterator,
nskConverter, nskMacro, nskTemplate, nskField, nskConverter, nskMacro, nskTemplate, nskField,
nskEnumField, nskForVar, nskModule, nskLabel, nskEnumField, nskForVar, nskModule, nskLabel,
nskStub nskStub
TNimSymKinds* = set[TNimrodSymKind] TNimSymKinds* = set[TNimrodSymKind]
type
TNimrodIdent* = object of TObject
## represents a Nimrod identifier in the AST
TNimrodSymbol {.final.} = object # hidden
TNimrodType {.final.} = object # hidden
PNimrodType* {.compilerproc.} = ref TNimrodType
## represents a Nimrod type in the compiler; currently this is not very
## useful as there is no API to deal with Nimrod types.
PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
## *ident*.
const
nnkLiterals* = {nnkCharLit..nnkNilLit}
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit}
proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
## get `n`'s `i`'th child.
proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
## set `n`'s `i`'th child to `child`.
proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}
## constructs an identifier from the string `s`
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
## converts a Nimrod identifier to a string
proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".}
## converts a Nimrod symbol to a string
proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.} type
## compares two Nimrod identifiers TNimrodIdent* = object of TObject
## represents a Nimrod identifier in the AST
proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}
## compares two Nimrod nodes TNimrodSymbol {.final.} = object # hidden
PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
proc len*(n: PNimrodNode): int {.magic: "NLen".} ## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
## returns the number of children of `n`. ## *ident*.
proc add*(father, child: PNimrodNode) {.magic: "NAdd".} const
## adds the `child` to the `father` node nnkLiterals* = {nnkCharLit..nnkNilLit}
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
proc add*(father: PNimrodNode, children: varargs[PNimrodNode]) {. nnkCallStrLit}
magic: "NAddMultiple".}
## adds each child of `children` to the `father` node proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
## get `n`'s `i`'th child.
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
## deletes `n` children of `father` starting at index `idx`. proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
## set `n`'s `i`'th child to `child`.
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
## returns the `kind` of the node `n`. proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}
## constructs an identifier from the string `s`
proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".} proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".} ## converts a Nimrod identifier to a string
proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
proc typ*(n: PNimrodNode): PNimrodType {.magic: "NGetType".} proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".}
proc strVal*(n: PNimrodNode): string {.magic: "NStrVal".} ## converts a Nimrod symbol to a string
proc `intVal=`*(n: PNimrodNode, val: biggestInt) {.magic: "NSetIntVal".} proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}
proc `floatVal=`*(n: PNimrodNode, val: biggestFloat) {.magic: "NSetFloatVal".} ## compares two Nimrod identifiers
proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".} proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}
proc `typ=`*(n: PNimrodNode, typ: PNimrodType) {.magic: "NSetType".} ## compares two Nimrod nodes
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
proc len*(n: PNimrodNode): int {.magic: "NLen".}
proc newNimNode*(kind: TNimrodNodeKind, ## returns the number of children of `n`.
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".} ## adds the `child` to the `father` node
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
proc add*(father: PNimrodNode, children: varargs[PNimrodNode]) {.
proc error*(msg: string) {.magic: "NError".} magic: "NAddMultiple".}
## writes an error message at compile time ## adds each child of `children` to the `father` node
proc warning*(msg: string) {.magic: "NWarning".} proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
## writes a warning message at compile time ## deletes `n` children of `father` starting at index `idx`.
proc hint*(msg: string) {.magic: "NHint".} proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
## writes a hint message at compile time ## returns the `kind` of the node `n`.
proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} = proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
## creates a string literal node from `s` proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
result = newNimNode(nnkStrLit) proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}
result.strVal = s proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType".}
proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} = proc strVal*(n: PNimrodNode): string {.magic: "NStrVal".}
## creates a int literal node from `i`
result = newNimNode(nnkIntLit) proc `intVal=`*(n: PNimrodNode, val: biggestInt) {.magic: "NSetIntVal".}
result.intVal = i proc `floatVal=`*(n: PNimrodNode, val: biggestFloat) {.magic: "NSetFloatVal".}
proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
proc newFloatLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} = proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
## creates a float literal node from `f` proc `typ=`*(n: PNimrodNode, typ: typedesc) {.magic: "NSetType".}
result = newNimNode(nnkFloatLit) proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
result.floatVal = f
proc newNimNode*(kind: TNimrodNodeKind,
proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} = n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
## creates an identifier node from `i`
result = newNimNode(nnkIdent) proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
result.ident = i proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
proc newIdentNode*(i: string): PNimrodNode {.compileTime.} = proc error*(msg: string) {.magic: "NError".}
## creates an identifier node from `i` ## writes an error message at compile time
result = newNimNode(nnkIdent)
result.ident = !i proc warning*(msg: string) {.magic: "NWarning".}
## writes a warning message at compile time
proc hint*(msg: string) {.magic: "NHint".}
## writes a hint message at compile time
proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
## creates a string literal node from `s`
result = newNimNode(nnkStrLit)
result.strVal = s
proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =
## creates a int literal node from `i`
result = newNimNode(nnkIntLit)
result.intVal = i
proc newFloatLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
## creates a float literal node from `f`
result = newNimNode(nnkFloatLit)
result.floatVal = f
proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = i
proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = !i
type type
TBindSymRule* = enum ## specifies how ``bindSym`` behaves TBindSymRule* = enum ## specifies how ``bindSym`` behaves
@ -212,157 +206,157 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
## returned even if the symbol is not ambiguous. ## returned even if the symbol is not ambiguous.
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.
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} = proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
## converts the AST `n` to the concrete Nimrod code and wraps that ## converts the AST `n` to the concrete Nimrod code and wraps that
## in a string literal node ## in a string literal node
return newStrLitNode(repr(n)) return newStrLitNode(repr(n))
proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".} proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}
## returns the position the node appears in the original source file ## returns the position the node appears in the original source file
## in the form filename(line, col) ## in the form filename(line, col)
proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst".} proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst".}
## Compiles the passed string to its AST representation. ## Compiles the passed string to its AST representation.
## Expects a single expression. ## Expects a single expression.
proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst".} proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst".}
## Compiles the passed string to its AST representation. ## Compiles the passed string to its AST representation.
## Expects one or more statements. ## Expects one or more statements.
proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".} proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
## Obtains the AST nodes returned from a macro or template invocation. ## Obtains the AST nodes returned from a macro or template invocation.
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
## ##
## macro FooMacro() = ## macro FooMacro() =
## var ast = getAst(BarTemplate()) ## var ast = getAst(BarTemplate())
template emit*(s: expr): stmt = template emit*(s: expr): stmt =
## accepts a single string argument and treats it as nimrod code ## accepts a single string argument and treats it as nimrod code
## that should be inserted verbatim in the program ## that should be inserted verbatim in the program
## Example: ## Example:
## ##
## emit("echo " & '"' & "hello world".toUpper & '"') ## emit("echo " & '"' & "hello world".toUpper & '"')
## ##
block: block:
const evaluated = s const evaluated = s
eval: result = evaluated.parseStmt eval: result = evaluated.parseStmt
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing
## macros that check the AST that is passed to them.
if n.kind != k: error("macro expects a node of kind: " & repr(k))
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
## checks that `n` has at least `min` children. If this is not the case,
## compilation aborts with an error message. This is useful for writing
## macros that check its number of arguments.
if n.len < min: error("macro expects a node with " & $min & " children")
proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
## checks that `n` has exactly `len` children. If this is not the case,
## compilation aborts with an error message. This is useful for writing
## macros that check its number of arguments.
if n.len != len: error("macro expects a node with " & $len & " children")
proc newCall*(theProc: PNimrodNode, proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} = ## checks that `n` is of kind `k`. If this is not the case,
## produces a new call node. `theProc` is the proc that is called with ## compilation aborts with an error message. This is useful for writing
## the arguments ``args[0..]``. ## macros that check the AST that is passed to them.
result = newNimNode(nnkCall) if n.kind != k: error("macro expects a node of kind: " & repr(k))
result.add(theProc)
result.add(args) proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
## checks that `n` has at least `min` children. If this is not the case,
proc newCall*(theProc: TNimrodIdent, ## compilation aborts with an error message. This is useful for writing
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} = ## macros that check its number of arguments.
## produces a new call node. `theProc` is the proc that is called with if n.len < min: error("macro expects a node with " & $min & " children")
## the arguments ``args[0..]``.
result = newNimNode(nnkCall) proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
result.add(newIdentNode(theProc)) ## checks that `n` has exactly `len` children. If this is not the case,
result.add(args) ## compilation aborts with an error message. This is useful for writing
## macros that check its number of arguments.
proc newCall*(theProc: string, if n.len != len: error("macro expects a node with " & $len & " children")
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
## produces a new call node. `theProc` is the proc that is called with proc newCall*(theProc: PNimrodNode,
## the arguments ``args[0..]``. args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
result = newNimNode(nnkCall) ## produces a new call node. `theProc` is the proc that is called with
result.add(newIdentNode(theProc)) ## the arguments ``args[0..]``.
result.add(args) result = newNimNode(nnkCall)
result.add(theProc)
proc nestList*(theProc: TNimrodIdent, result.add(args)
x: PNimrodNode): PNimrodNode {.compileTime.} =
## nests the list `x` into a tree of call expressions: proc newCall*(theProc: TNimrodIdent,
## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``. args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
var L = x.len ## produces a new call node. `theProc` is the proc that is called with
result = newCall(theProc, x[L-2], x[L-1]) ## the arguments ``args[0..]``.
var a = result result = newNimNode(nnkCall)
for i in countdown(L-3, 0): result.add(newIdentNode(theProc))
a = newCall(theProc, x[i], copyNimTree(a)) result.add(args)
proc treeRepr*(n: PNimrodNode): string {.compileTime.} = proc newCall*(theProc: string,
## Convert the AST `n` to a human-readable tree-like string. args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
## ## produces a new call node. `theProc` is the proc that is called with
## See also `repr` and `lispRepr`. ## the arguments ``args[0..]``.
proc traverse(res: var string, level: int, n: PNimrodNode) = result = newNimNode(nnkCall)
for i in 0..level-1: res.add " " result.add(newIdentNode(theProc))
res.add(($n.kind).substr(3)) result.add(args)
case n.kind proc nestList*(theProc: TNimrodIdent,
of nnkEmpty: nil # same as nil node in this representation x: PNimrodNode): PNimrodNode {.compileTime.} =
of nnkNilLit: res.add(" nil") ## nests the list `x` into a tree of call expressions:
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) var L = x.len
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal) result = newCall(theProc, x[L-2], x[L-1])
of nnkIdent: res.add(" !\"" & $n.ident & '"') var a = result
for i in countdown(L-3, 0):
a = newCall(theProc, x[i], copyNimTree(a))
proc treeRepr*(n: PNimrodNode): string {.compileTime.} =
## Convert the AST `n` to a human-readable tree-like string.
##
## See also `repr` and `lispRepr`.
proc traverse(res: var string, level: int, n: PNimrodNode) =
for i in 0..level-1: res.add " "
res.add(($n.kind).substr(3))
case n.kind
of nnkEmpty: nil # same as nil node in this representation
of nnkNilLit: res.add(" nil")
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
of nnkIdent: res.add(" !\"" & $n.ident & '"')
of nnkSym: res.add(" \"" & $n.symbol & '"') of nnkSym: res.add(" \"" & $n.symbol & '"')
of nnkNone: assert false of nnkNone: assert false
else: else:
for j in 0..n.len-1: for j in 0..n.len-1:
res.add "\n" res.add "\n"
traverse(res, level + 1, n[j]) traverse(res, level + 1, n[j])
result = "" result = ""
traverse(result, 0, n) traverse(result, 0, n)
proc lispRepr*(n: PNimrodNode): string {.compileTime.} = proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
## Convert the AST `n` to a human-readable lisp-like string, ## Convert the AST `n` to a human-readable lisp-like string,
## ##
## See also `repr` and `treeRepr`. ## See also `repr` and `treeRepr`.
result = ($n.kind).substr(3) result = ($n.kind).substr(3)
add(result, "(") add(result, "(")
case n.kind case n.kind
of nnkEmpty: nil # same as nil node in this representation of nnkEmpty: nil # same as nil node in this representation
of nnkNilLit: add(result, "nil") of nnkNilLit: add(result, "nil")
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal) of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
of nnkIdent: add(result, "!\"" & $n.ident & '"') of nnkIdent: add(result, "!\"" & $n.ident & '"')
of nnkSym, nnkNone: assert false of nnkSym, nnkNone: assert false
else: else:
add(result, lispRepr(n[0])) add(result, lispRepr(n[0]))
for j in 1..n.len-1: for j in 1..n.len-1:
add(result, ", ") add(result, ", ")
add(result, lispRepr(n[j])) add(result, lispRepr(n[j]))
add(result, ")") add(result, ")")
macro dumpTree*(s: stmt): stmt = echo s.treeRepr macro dumpTree*(s: stmt): stmt = echo s.treeRepr
## Accepts a block of nimrod code and prints the parsed abstract syntax ## Accepts a block of nimrod code and prints the parsed abstract syntax
## tree using the `toTree` function. Printing is done *at compile time*. ## tree using the `toTree` function. Printing is done *at compile time*.
## ##
## You can use this as a tool to explore the Nimrod's abstract syntax ## You can use this as a tool to explore the Nimrod's abstract syntax
## tree and to discover what kind of nodes must be created to represent ## tree and to discover what kind of nodes must be created to represent
## a certain expression/statement. ## a certain expression/statement.
macro dumpLisp*(s: stmt): stmt = echo s.lispRepr macro dumpLisp*(s: stmt): stmt = echo s.lispRepr
## Accepts a block of nimrod code and prints the parsed abstract syntax ## Accepts a block of nimrod code and prints the parsed abstract syntax
## tree using the `toLisp` function. Printing is done *at compile time*. ## tree using the `toLisp` function. Printing is done *at compile time*.
## ##
## See `dumpTree`. ## See `dumpTree`.

View file

@ -149,7 +149,7 @@ else: # UNIX-like operating system
FileSystemCaseSensitive* = true FileSystemCaseSensitive* = true
ExeExt* = "" ExeExt* = ""
ScriptExt* = "" ScriptExt* = ""
DynlibFormat* = "lib$1.so" DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
when defined(macosx) or defined(bsd): when defined(macosx) or defined(bsd):
var var

View file

@ -51,10 +51,11 @@ type
`nil` {.magic: "Nil".} `nil` {.magic: "Nil".}
expr* {.magic: Expr.} ## meta type to denote an expression (for templates) expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates) stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
typeDesc* {.magic: TypeDesc.} ## meta type to denote typeDesc* {.magic: TypeDesc.} ## meta type to denote a type description
## a type description (for templates) void* {.magic: "VoidType".} ## meta type to denote the absense of any type
void* {.magic: "VoidType".} ## meta type to denote the absense of any type auto* = expr
any* = distinct auto
TSignedInt* = int|int8|int16|int32|int64 TSignedInt* = int|int8|int16|int32|int64
## type class matching all signed integer types ## type class matching all signed integer types
@ -111,6 +112,11 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
## creates a new object of type ``T`` and returns a safe (traced) ## creates a new object of type ``T`` and returns a safe (traced)
## reference to it in ``a``. ## reference to it in ``a``.
proc new(T: typedesc): ref T =
## creates a new object of type ``T`` and returns a safe (traced)
## reference to it as result value
new(result)
proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.} proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
## leaked implementation detail. Do not use. ## leaked implementation detail. Do not use.
@ -538,7 +544,7 @@ proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
## checking is turned on). ## checking is turned on).
type type
IntMax32 = distinct int|int8|int16|int32 IntMax32 = int|int8|int16|int32
proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.} proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.}
proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.} proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.}
@ -1315,11 +1321,10 @@ iterator items*(a: cstring): char {.inline.} =
yield a[i] yield a[i]
inc(i) inc(i)
when not defined(booting): iterator items*(E: typedesc[enum]): E =
iterator items*(E: typedesc[enum]): E = ## iterates over the values of the enum ``E``.
## iterates over the values of the enum ``E``. for v in low(E)..high(E):
for v in low(E)..high(E): yield v
yield v
iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} = iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} =
## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
@ -2111,7 +2116,7 @@ proc `/`*(x, y: int): float {.inline, noSideEffect.} =
## integer division that results in a float. ## integer division that results in a float.
result = toFloat(x) / toFloat(y) result = toFloat(x) / toFloat(y)
template `-|`(b, s: expr): expr = template `-|`*(b, s: expr): expr =
(if b >= 0: b else: s.len + b) (if b >= 0: b else: s.len + b)
proc `[]`*(s: string, x: TSlice[int]): string {.inline.} = proc `[]`*(s: string, x: TSlice[int]): string {.inline.} =

42
tests/compile/tisop.nim Normal file
View file

@ -0,0 +1,42 @@
import typetraits
type
TRecord = (tuple) or (object)
TFoo[T, U] = object
x: int
when T is string:
y: float
else:
y: string
when U is TRecord:
z: float
E = enum A, B, C
macro m(t: typedesc): typedesc =
if t is enum:
result = string
else:
result = int
var f: TFoo[int, int]
static: assert(f.y.type.name == "string")
when compiles(f.z):
{.error: "Foo should not have a `z` field".}
proc p(a, b) =
when a.type is int:
static: assert false
var f: TFoo[m(a.type), b.type]
static:
assert f.x.type.name == "int"
assert f.y.type.name == "float"
assert f.z.type.name == "float"
p(A, f)

View file

@ -0,0 +1,12 @@
import macros, typetraits
macro checkType(ex, expected: expr): stmt {.immediate.} =
var t = ex.typ
assert t.name == expected.strVal
proc voidProc = echo "hello"
proc intProc(a, b): int = 10
checkType(voidProc(), "void")
checkType(intProc(10, 20.0), "int")
checkType(noproc(10, 20.0), "Error Type")

View file

@ -2,8 +2,8 @@ type
TFoo[T] = object TFoo[T] = object
val: T val: T
T1 = distinct expr T1 = expr
T2 = distinct expr T2 = expr
proc takesExpr(x, y) = proc takesExpr(x, y) =
echo x, y echo x, y

View file

@ -0,0 +1,30 @@
discard """
file: "tcaseexpr1.nim"
line: 29
errormsg: "type mismatch: got (string) but expected 'int'"
line: 23
errormsg: "not all cases are covered"
"""
type
E = enum A, B, C
proc foo(x): auto =
return case x
of 1..9: "digit"
else: "number"
var r = foo(10)
var x = C
var t1 = case x:
of A: "a"
of B: "b"
var t2 = case x:
of A: 10
of B, C: "23"

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "system.nim" file: "system.nim"
line: 643 line: 649
errormsg: "type mismatch" errormsg: "type mismatch"
""" """

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "tcasestm.nim" file: "tcasestm.nim"
output: "ayyy" output: "ayyydd"
""" """
# Test the case statement # Test the case statement
@ -22,16 +22,18 @@ of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
of "cc", "hash", "when": nil of "cc", "hash", "when": nil
of "will", "it", "finally", "be", "generated": nil of "will", "it", "finally", "be", "generated": nil
case i var z = case i
of 1..5, 8, 9: nil of 1..5, 8, 9: "aa"
of 6, 7: nil of 6, 7: "bb"
elif x == "Ha": elif x == "Ha":
nil "cc"
elif x == "yyy": elif x == "yyy":
write(stdout, x) write(stdout, x)
else: "dd"
nil else:
"zz"
echo z
#OUT ayyy #OUT ayyy

View file

@ -0,0 +1,33 @@
discard """
msg: "instantiated for string\ninstantiated for int\ninstantiated for bool"
output: "int\nseq[string]\nA\nB\n100\ntrue"
"""
import typetraits
proc plus(a, b): auto = a + b
proc makePair(a, b): auto = (first: a, second: b)
proc `+`(a, b: string): seq[string] = @[a, b]
var i = plus(10, 20)
var s = plus("A", "B")
var p = makePair("key", 100)
static: assert p[0].type is string
echo i.type.name
echo s.type.name
proc inst(a): auto =
static: echo "instantiated for ", a.type.name
result = a
echo inst("A")
echo inst("B")
echo inst(100)
echo inst(true)
# XXX: [string, tyGenericParam] is cached instead of [string, string]
# echo inst[string, string]("C")

View file

@ -2,6 +2,39 @@
News News
==== ====
2012-XX-XX Version 0.9.XX released
==================================
Version 0.8.XX has been released! Get it `here <download.html>`_.
Bugfixes
--------
Library Additions
-----------------
Changes affecting backwards compatibility
-----------------------------------------
Compiler Additions
------------------
Language Additions
------------------
- ``case expressions`` are now supported.
- table constructors now mimic more closely the syntax of case... of...
- Nimrod can now infer the return type of a proc from its body
2012-09-23 Version 0.9.0 released 2012-09-23 Version 0.9.0 released
================================= =================================