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

This commit is contained in:
Araq
2012-11-11 22:05:04 +01:00
8 changed files with 130 additions and 15 deletions

View File

@@ -415,7 +415,7 @@ type
mDefined, mDefinedInScope, mCompiles,
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf,
mEcho, mShallowCopy, mSlurp, mStaticExec,
mParseExprToAst, mParseStmtToAst, mExpandToAst,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
@@ -723,6 +723,7 @@ const
nkLambdaKinds* = {nkLambda, nkDo}
nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
nkStrKinds* = {nkStrLit..nkTripleStrLit}
skLocalVars* = {skVar, skLet, skForVar, skParam}

View File

@@ -898,7 +898,7 @@ proc evalIsOp*(n: PNode): PNode =
result.typ = n.typ
proc expectString(n: PNode) =
if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
if n.kind notin nkStrKinds:
GlobalError(n.info, errStringLiteralExpected)
proc evalSlurp*(e: PNode, module: PSym): PNode =

View File

@@ -457,6 +457,9 @@ proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} =
fileInfos.add(newFileInfo("", "command line"))
var gCmdLineInfo* = newLineInfo(int32(0), 1, 1)
fileInfos.add(newFileInfo("", "compilation artifact"))
var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1)
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
raise newException(ERecoverableError, msg)

View File

@@ -45,6 +45,7 @@ proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc fixImmediateParams(n: PNode): PNode
proc activate(c: PContext, n: PNode)
proc semQuoteAst(c: PContext, n: PNode): PNode
proc typeMismatch(n: PNode, formal, actual: PType) =
if formal.kind != tyError and actual.kind != tyError:

View File

@@ -1241,25 +1241,100 @@ proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
LocalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
result = errorSym(c, n)
proc expectString(c: PContext, n: PNode): string =
var n = semConstExpr(c, n)
if n.kind in nkStrKinds:
return n.strVal
else:
LocalError(n.info, errStringLiteralExpected)
proc getMagicSym(magic: TMagic): PSym =
result = newSym(skProc, getIdent($magic), GetCurrOwner(), gCodegenLineInfo)
result.magic = magic
proc newAnonSym(kind: TSymKind, info: TLineInfo,
owner = getCurrOwner()): PSym =
result = newSym(kind, idAnon, owner, info)
result.flags = { sfGenSym }
proc semExpandToAst(c: PContext, n: PNode): PNode =
var macroCall = n[1]
var expandedSym = expectMacroOrTemplateCall(c, macroCall)
macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
markUsed(n, expandedSym)
for i in countup(1, macroCall.len-1):
macroCall.sons[i] = semExprWithType(c, macroCall[i], {})
# Preserve the magic symbol in order to be handled in evals.nim
InternalAssert n.sons[0].sym.magic == mExpandToAst
n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType
result = n
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
flags: TExprFlags): PNode =
flags: TExprFlags = {}): PNode =
if sonsLen(n) == 2:
var macroCall = n[1]
var expandedSym = expectMacroOrTemplateCall(c, macroCall)
macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
markUsed(n, expandedSym)
for i in countup(1, macroCall.len-1):
macroCall.sons[i] = semExprWithType(c, macroCall[i], {})
# Preserve the magic symbol in order to be handled in evals.nim
n.sons[0] = newSymNode(magicSym, n.info)
n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType
result = n
result = semExpandToAst(c, n)
else:
result = semDirectOp(c, n, flags)
proc processQuotations(n: var PNode, op: string,
quotes: var seq[PNode],
ids: var seq[PNode]) =
template returnQuote(q) =
quotes.add q
n = newIdentNode(getIdent($quotes.len), n.info)
ids.add n
return
if n.kind == nkPrefix:
checkSonsLen(n, 2)
if n[0].kind == nkIdent:
var examinedOp = n[0].ident.s
if examinedOp == op:
returnQuote n[1]
elif examinedOp.startsWith(op):
n.sons[0] = newIdentNode(getIdent(examinedOp.substr(op.len)), n.info)
elif n.kind == nkAccQuoted and op == "``":
returnQuote n[0]
if not n.isAtom:
for i in 0 .. <n.len:
processQuotations(n.sons[i], op, quotes, ids)
proc semQuoteAst(c: PContext, n: PNode): PNode =
InternalAssert n.len == 2 or n.len == 3
# We transform the do block into a template with a param for
# each interpolation. We'll pass this template to getAst.
var
doBlk = n{-1}
op = if n.len == 3: expectString(c, n[1]) else: "``"
quotes = newSeq[PNode](1)
# the quotes will be added to a nkCall statement
# leave some room for the callee symbol
ids = newSeq[PNode]()
# this will store the generated param names
internalAssert doBlk.kind == nkDo
processQuotations(doBlk.sons[bodyPos], op, quotes, ids)
doBlk.sons[namePos] = newAnonSym(skTemplate, n.info).newSymNode
if ids.len > 0:
doBlk[paramsPos].sons.setLen(2)
doBlk[paramsPos].sons[0] = getSysSym("stmt").newSymNode # return type
ids.add getSysSym("expr").newSymNode # params type
ids.add emptyNode # no default value
doBlk[paramsPos].sons[1] = newNode(nkIdentDefs, n.info, ids)
var tmpl = semTemplateDef(c, doBlk)
quotes[0] = tmpl[namePos]
result = newNode(nkCall, n.info, @[
getMagicSym(mExpandToAst).newSymNode,
newNode(nkCall, n.info, quotes)])
result = semExpandToAst(c, result)
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# watch out, hacks ahead:
let oldErrorCount = msgs.gErrorCounter
@@ -1335,6 +1410,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mEcho: result = semEcho(c, setMs(n, s))
of mShallowCopy: result = semShallowCopy(c, n, flags)
of mExpandToAst: result = semExpandToAst(c, n, s, flags)
of mQuoteAst: result = semQuoteAst(c, n)
else: result = semDirectOp(c, n, flags)
proc semIfExpr(c: PContext, n: PNode): PNode =

View File

@@ -234,6 +234,35 @@ proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
## macro FooMacro() =
## var ast = getAst(BarTemplate())
proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
## Quasi-quoting operator.
## Accepts an expression or a block and returns the AST that represents it.
## Within the quoted AST, you are able to interpolate PNimrodNode expressions
## from the surrounding scope. If no operator is given, quoting is done using
## backticks. Otherwise, the given operator must be used as a prefix operator
## for any interpolated expression. The original meaning of the interpolation
## operator may be obtained by escaping it (by prefixing it with itself):
## e.g. `@` is escaped as `@@`, `@@` is escaped as `@@@` and so on.
##
## Example:
##
## macro check(ex: expr): stmt =
## # this is a simplified version of the check macro from the
## # unittest module.
##
## # If there is a failed check, we want to make it easy for
## # the user to jump to the faulty line in the code, so we
## # get the line info here:
## var info = ex.lineinfo
##
## # We will also display the code string of the failed check:
## var expString = ex.toStrLit
##
## # Finally we compose the code to implement the check:
## result = quote do:
## if not `ex`:
## echo `info` & ": Check failed: " & `expString`
template emit*(e: expr[string]): stmt =
## accepts a single string argument and treats it as nimrod code
## that should be inserted verbatim in the program

View File

@@ -364,6 +364,10 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
## This is equivalent to ``s = @[]; setlen(s, len)``, but more
## efficient since no reallocation is needed.
proc newSeq*[T](len = 0): seq[T] =
## creates a new sequence of type ``seq[T]`` with length ``len``.
newSeq(result, len)
proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
magic: "LengthOpenArray", noSideEffect.}
proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}

View File

@@ -16,6 +16,7 @@ Library Additions
-----------------
- Added ``system.onRaise`` to support a condition system.
- Added ``macros.quote`` for AST quasi-quoting.
Changes affecting backwards compatibility