diff options
-rwxr-xr-x | compiler/ast.nim | 3 | ||||
-rwxr-xr-x | compiler/evals.nim | 2 | ||||
-rwxr-xr-x | compiler/msgs.nim | 3 | ||||
-rwxr-xr-x | compiler/sem.nim | 1 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 100 | ||||
-rwxr-xr-x | lib/core/macros.nim | 29 | ||||
-rwxr-xr-x | lib/system.nim | 4 | ||||
-rwxr-xr-x | web/news.txt | 1 |
8 files changed, 129 insertions, 14 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 88cf5fd61..46be0379f 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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} diff --git a/compiler/evals.nim b/compiler/evals.nim index c6248e823..0d960c952 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -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 = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 50d7e2904..45075cba8 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -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) diff --git a/compiler/sem.nim b/compiler/sem.nim index bd1ad1468..911eafb08 100755 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2b39f6190..a047fed20 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1241,25 +1241,100 @@ proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym = LocalError(n.info, errXisNoMacroOrTemplate, n.renderTree) result = errorSym(c, n) -proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, - flags: TExprFlags): PNode = - if sonsLen(n) == 2: - var macroCall = n[1] - var expandedSym = expectMacroOrTemplateCall(c, macroCall) +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 } - macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) - markUsed(n, expandedSym) +proc semExpandToAst(c: PContext, n: PNode): PNode = + var macroCall = n[1] + var expandedSym = expectMacroOrTemplateCall(c, macroCall) - for i in countup(1, macroCall.len-1): - macroCall.sons[i] = semExprWithType(c, macroCall[i], {}) + macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) + markUsed(n, expandedSym) - # Preserve the magic symbol in order to be handled in evals.nim + 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 = + if sonsLen(n) == 2: 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 = diff --git a/lib/core/macros.nim b/lib/core/macros.nim index be6aecab5..b80de27fa 100755 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -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 diff --git a/lib/system.nim b/lib/system.nim index 5a4c491e1..b681ed51e 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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.} diff --git a/web/news.txt b/web/news.txt index c7f3077e1..b73a32268 100755 --- a/web/news.txt +++ b/web/news.txt @@ -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 |