diff --git a/compiler/ast.nim b/compiler/ast.nim index 79adafcf8..a8176501f 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -441,7 +441,7 @@ type mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, - mNBindSym, + mNBindSym, mNCallSite, mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo diff --git a/compiler/evals.nim b/compiler/evals.nim index 4ec6cb4d7..bb0e5936d 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -38,6 +38,7 @@ type module*: PSym tos*: PStackFrame # top of stack lastException*: PNode + callsite: PNode # for 'callsite' magic mode*: TEvalMode globals*: TIdNodeTable # state of global vars @@ -74,7 +75,7 @@ proc popStackFrame*(c: PEvalContext) {.inline.} = if c.tos != nil: c.tos = c.tos.next else: InternalError("popStackFrame") -proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode +proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = @@ -106,8 +107,11 @@ proc stackTrace(c: PEvalContext, n: PNode, msg: TMsgKind, arg = "") = proc isSpecial(n: PNode): bool {.inline.} = result = n.kind == nkExceptBranch -proc myreset(n: PNode) {.inline.} = - when defined(system.reset): reset(n[]) +proc myreset(n: PNode) = + when defined(system.reset): + var oldInfo = n.info + reset(n[]) + n.info = oldInfo proc evalIf(c: PEvalContext, n: PNode): PNode = var i = 0 @@ -245,6 +249,11 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = of tyObject: result = newNodeIT(nkPar, info, t) getNullValueAux(t.n, result) + # initialize inherited fields: + var base = t.sons[0] + while base != nil: + getNullValueAux(skipTypes(base, skipPtrs).n, result) + base = base.sons[0] of tyArray, tyArrayConstr: result = newNodeIT(nkBracket, info, t) for i in countup(0, int(lengthOrd(t)) - 1): @@ -925,7 +934,7 @@ proc evalExpandToAst(c: PEvalContext, original: PNode): PNode = # we want to replace it with nkIdent node featuring # the original unmangled macro name. macroCall.sons[0] = newIdentNode(expandedSym.name, expandedSym.info) - result = evalMacroCall(c, macroCall, expandedSym) + result = evalMacroCall(c, macroCall, original, expandedSym) else: InternalError(macroCall.info, "ExpandToAst: expanded symbol is no macro or template") @@ -1166,10 +1175,13 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = of mIdentToStr: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return - if result.kind != nkIdent: InternalError(n.info, "no ident node") var a = result result = newNodeIT(nkStrLit, n.info, n.typ) - result.strVal = a.ident.s + if a.kind == nkSym: + result.strVal = a.sym.name.s + else: + if a.kind != nkIdent: InternalError(n.info, "no ident node") + result.strVal = a.ident.s of mEqIdent: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return @@ -1226,6 +1238,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = var a = result result = newNodeIT(nkStrLit, n.info, n.typ) result.strVal = newString(0) + of mNCallSite: + if c.callsite != nil: result = c.callsite + else: stackTrace(c, n, errFieldXNotFound, "callsite") else: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return @@ -1355,24 +1370,31 @@ proc evalConstExpr*(module: PSym, e: PNode): PNode = proc evalStaticExpr*(module: PSym, e: PNode): PNode = result = evalConstExprAux(module, e, emStatic) -proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode = +proc setupMacroParam(x: PNode): PNode = + result = x + if result.kind == nkHiddenStdConv: result = result.sons[1] + +proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode = # XXX GlobalError() is ugly here, but I don't know a better solution for now inc(evalTemplateCounter) - if evalTemplateCounter > 100: + if evalTemplateCounter > 100: GlobalError(n.info, errTemplateInstantiationTooNested) - #inc genSymBaseId + c.callsite = nOrig var s = newStackFrame() s.call = n - setlen(s.params, 2) + setlen(s.params, n.len) + # return value: s.params[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) - s.params[1] = n + # setup parameters: + for i in 1 .. < n.len: s.params[i] = setupMacroParam(n.sons[i]) pushStackFrame(c, s) discard eval(c, sym.getBody) result = s.params[0] popStackFrame(c) if cyclicTree(result): GlobalError(n.info, errCyclicTree) dec(evalTemplateCounter) + c.callsite = nil proc myOpen(module: PSym, filename: string): PPassContext = var c = newEvalContext(module, filename, emRepl) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index c24a22fd5..b6b34287a 100755 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -774,7 +774,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"') else: gsub(g, n.sons[1]) - of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: gsub(g, n.sons[0]) + of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: gsub(g, n.sons[1]) of nkCast: put(g, tkCast, "cast") put(g, tkBracketLe, "[") diff --git a/compiler/sem.nim b/compiler/sem.nim index 34b6f1f78..d92c1657d 100755 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -81,7 +81,7 @@ proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode -proc semMacroExpr(c: PContext, n: PNode, sym: PSym, +proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, semCheck: bool = true): PNode proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode @@ -127,14 +127,14 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) dec(evalTemplateCounter) -proc semMacroExpr(c: PContext, n: PNode, sym: PSym, +proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, semCheck: bool = true): PNode = markUsed(n, sym) if sym == c.p.owner: GlobalError(n.info, errRecursiveDependencyX, sym.name.s) if c.evalContext == nil: c.evalContext = newEvalContext(c.module, "", emStatic) - result = evalMacroCall(c.evalContext, n, sym) + result = evalMacroCall(c.evalContext, n, nOrig, sym) if semCheck: result = semAfterMacroCall(c, result, sym) proc forceBool(c: PContext, n: PNode): PNode = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 06014bf6f..9219cacf6 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -98,7 +98,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = else: result = newSymNode(s, n.info) else: result = newSymNode(s, n.info) - of skMacro: result = semMacroExpr(c, n, s) + of skMacro: result = semMacroExpr(c, n, n, s) of skTemplate: result = semTemplateExpr(c, n, s) of skVar, skLet, skResult, skParam, skForVar: markUsed(n, s) @@ -705,7 +705,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = return errorNode(c, n) let callee = result.sons[0].sym case callee.kind - of skMacro: result = semMacroExpr(c, nOrig, callee) + of skMacro: result = semMacroExpr(c, result, nOrig, callee) of skTemplate: result = semTemplateExpr(c, nOrig, callee) else: fixAbstractType(c, result) @@ -1398,7 +1398,8 @@ proc semBlockExpr(c: PContext, n: PNode): PNode = closeScope(c.tab) Dec(c.p.nestedBlockCounter) -proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode = +proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode = + # XXX why no overloading here? checkMinSonsLen(n, 2) var a: PNode if isCallExpr(n.sons[0]): a = n.sons[0].sons[0] @@ -1407,7 +1408,7 @@ proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode = if s != nil: case s.kind of skMacro: - result = semMacroExpr(c, n, s, semCheck) + result = semMacroExpr(c, n, n, s, semCheck) of skTemplate: # transform # nkMacroStmt(nkCall(a...), stmt, b...) @@ -1499,16 +1500,16 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if s != nil: case s.kind of skMacro: - if false and sfImmediate notin s.flags: # XXX not yet enabled + if sfImmediate notin s.flags: result = semDirectOp(c, n, flags) else: - result = semMacroExpr(c, n, s) + result = semMacroExpr(c, n, n, s) of skTemplate: if sfImmediate notin s.flags: result = semDirectOp(c, n, flags) else: result = semTemplateExpr(c, n, s) - of skType: + of skType: # XXX think about this more (``set`` procs) if n.len == 2: result = semConv(c, n, s) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 849d04fb1..ea3eebc3a 100755 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -51,7 +51,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode = of skTemplate: result = semTemplateExpr(c, n, s, false) of skMacro: - result = semMacroExpr(c, n, s, false) + result = semMacroExpr(c, n, n, s, false) of skGenericParam: result = newSymNode(s, n.info) of skParam: @@ -97,7 +97,7 @@ proc semGenericStmt(c: PContext, n: PNode, incl(s.flags, sfUsed) case s.kind of skMacro: - result = semMacroExpr(c, n, s, false) + result = semMacroExpr(c, n, n, s, false) of skTemplate: result = semTemplateExpr(c, n, s, false) # BUGFIX: we must not return here, we need to do first phase of diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d12ad23cf..3a8424d9e 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -892,13 +892,10 @@ proc semConverterDef(c: PContext, n: PNode): PNode = proc semMacroDef(c: PContext, n: PNode): PNode = checkSonsLen(n, bodyPos + 1) - if n.sons[genericParamsPos].kind != nkEmpty: - LocalError(n.info, errNoGenericParamsAllowedForX, "macro") result = semProcAux(c, n, skMacro, macroPragmas) var s = result.sons[namePos].sym var t = s.typ if t.sons[0] == nil: LocalError(n.info, errXNeedsReturnType, "macro") - if sonsLen(t) != 2: LocalError(n.info, errXRequiresOneArgument, "macro") if n.sons[bodyPos].kind == nkEmpty: LocalError(n.info, errImplOfXexpected, s.name.s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5b6183005..8ec3c337c 100755 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -237,9 +237,11 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = return errorSym(c, n) if result.typ.kind != tyGenericParam: # XXX get rid of this hack! + var oldInfo = n.info reset(n[]) n.kind = nkSym n.sym = result + n.info = oldInfo else: LocalError(n.info, errIdentifierExpected) result = errorSym(c, n) @@ -549,7 +551,9 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = incl(result.flags, tfFinal) proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = - if kind == skMacro and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}: + if kind == skMacro: + # within a macro, every param has the type PNimrodNode! + # and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}: let nn = getSysSym"PNimrodNode" var a = copySym(param) a.typ = nn.typ @@ -779,7 +783,7 @@ proc semTypeFromMacro(c: PContext, n: PNode): PType = markUsed(n, sym) case sym.kind of skMacro: - result = semTypeNode(c, semMacroExpr(c, n, sym), nil) + result = semTypeNode(c, semMacroExpr(c, n, n, sym), nil) of skTemplate: result = semTypeNode(c, semTemplateExpr(c, n, sym), nil) else: diff --git a/doc/manual.txt b/doc/manual.txt index e10c934f1..df64649ed 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -3102,7 +3102,7 @@ a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates. Macros ------ -A `macro`:idx: is a special kind of low level template. They can be used +A `macro`:idx: is a special kind of low level template. Macros can be used to implement `domain specific languages`:idx:. Like templates, macros come in the 2 flavors *immediate* and *ordinary*. @@ -3129,12 +3129,12 @@ variable number of arguments: # ``macros`` module: import macros - macro debug(n: expr): stmt = + macro debug(n: varargs[expr]): stmt = # `n` is a Nimrod AST that contains the whole macro invocation # this macro returns a list of statements: result = newNimNode(nnkStmtList, n) # iterate over any argument that is passed to this macro: - for i in 1..n.len-1: + for i in 0..n.len-1: # add a call to the statement list that writes the expression; # `toStrLit` converts an AST to its string representation: add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) @@ -3167,6 +3167,11 @@ The macro call expands to: writeln(stdout, x) +Arguments that are passed to a ``varargs`` parameter are wrapped in an array +constructor expression. This is why ``debug`` iterates over all of ``n``'s +children. + + BindSym ~~~~~~~ @@ -3179,9 +3184,9 @@ builtin can be used for that: .. code-block:: nimrod import macros - macro debug(n: expr): stmt = + macro debug(n: varargs[expr]): stmt = result = newNimNode(nnkStmtList, n) - for i in 1..n.len-1: + for i in 0..n.len-1: # we can bind symbols in scope via 'bindSym': add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(n[i]))) add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": "))) diff --git a/doc/tut2.txt b/doc/tut2.txt index 96507bcb1..85a68c983 100755 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -67,7 +67,7 @@ Objects have access to their type at runtime. There is an person: TPerson assert(student of TStudent) # is true -Object fields that should be visible from outside the defining module, have to +Object fields that should be visible from outside the defining module have to be marked by ``*``. In contrast to tuples, different object types are never *equivalent*. New object types can only be defined within a type section. @@ -631,19 +631,19 @@ Expression Macros ----------------- The following example implements a powerful ``debug`` command that accepts a -variable number of arguments (this cannot be done with templates): +variable number of arguments: .. code-block:: nimrod # to work with Nimrod syntax trees, we need an API that is defined in the # ``macros`` module: import macros - macro debug(n: expr): stmt = - # `n` is a Nimrod AST that contains the whole macro expression + macro debug(n: varargs[expr]): stmt = + # `n` is a Nimrod AST that contains a list of expressions; # this macro returns a list of statements: result = newNimNode(nnkStmtList, n) # iterate over any argument that is passed to this macro: - for i in 1..n.len-1: + for i in 0..n.len-1: # add a call to the statement list that writes the expression; # `toStrLit` converts an AST to its string representation: result.add(newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) @@ -702,5 +702,3 @@ regular expressions: return tkOperator else: return tkUnknown - - diff --git a/lib/core/macros.nim b/lib/core/macros.nim index fac935430..610360fb2 100755 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -96,27 +96,25 @@ const nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit} -# Nodes should be reference counted to make the `copy` operation very fast! -# However, this is difficult to achieve: modify(n[0][1]) should propagate to -# its father. How to do this without back references? Hm, BS, it works without -# them. - -proc `[]`* (n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".} +proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".} ## get `n`'s `i`'th child. -proc `[]=`* (n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".} +proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".} ## set `n`'s `i`'th child to `child`. -proc `!` *(s: string): TNimrodIdent {.magic: "StrToIdent".} +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 `==`* (a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.} +proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".} + ## converts a Nimrod symbol to a string + +proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.} ## compares two Nimrod identifiers -proc `==`* (a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.} +proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.} ## compares two Nimrod nodes proc len*(n: PNimrodNode): int {.magic: "NLen".} @@ -209,6 +207,9 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {. ## returned or ``nkSym`` if the symbol is not ambiguous. ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is ## returned even if the symbol is not ambiguous. + +proc callsite*(): PNimrodNode {.magic: "NCallSite".} + ## returns the AST if the invokation expression that invoked this macro. proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} = ## converts the AST `n` to the concrete Nimrod code and wraps that @@ -246,15 +247,6 @@ template emit*(s: expr): stmt = block: const evaluated = s eval: result = evaluated.parseStmt - when false: - template once(x: expr): expr = - block: - const y = x - y - - macro `payload`(x: stmt): stmt = result = once(s).parseStmt - `payload`() - proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} = ## checks that `n` is of kind `k`. If this is not the case, @@ -312,27 +304,23 @@ 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)) - if n == nil: - res.add "nil" + 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 nnkNone: assert false else: - 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, nnkNone: assert false - else: - for j in 0..n.len-1: - res.add "\n" - traverse(res, level + 1, n[j]) + for j in 0..n.len-1: + res.add "\n" + traverse(res, level + 1, n[j]) result = "" traverse(result, 0, n) @@ -342,8 +330,6 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} = ## ## See also `repr` and `treeRepr`. - if n == nil: return "nil" - result = ($n.kind).substr(3) add(result, "(") @@ -363,7 +349,7 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} = add(result, ")") -macro dumpTree*(s: stmt): stmt = echo s[1].treeRepr +macro dumpTree*(s: stmt): stmt = echo s.treeRepr ## Accepts a block of nimrod code and prints the parsed abstract syntax ## tree using the `toTree` function. Printing is done *at compile time*. ## @@ -371,7 +357,7 @@ macro dumpTree*(s: stmt): stmt = echo s[1].treeRepr ## tree and to discover what kind of nodes must be created to represent ## a certain expression/statement. -macro dumpLisp*(s: stmt): stmt = echo s[1].lispRepr +macro dumpLisp*(s: stmt): stmt = echo s.lispRepr ## Accepts a block of nimrod code and prints the parsed abstract syntax ## tree using the `toLisp` function. Printing is done *at compile time*. ## diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index 86ee4159b..75af2c892 100755 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -15,11 +15,11 @@ ## ## .. code-block:: nimrod ## var nim = "Nimrod" -## echo h1(a(href="http://force7.de/nimrod", nim)) +## echo h1(a(href="http://nimrod-code.org", nim)) ## ## Writes the string:: ## -##