parameter passing works the same for macros and templates; use callsite magic to access the invokation AST

This commit is contained in:
Araq 2012-08-28 22:15:29 +02:00
commit b4844a189d
16 changed files with 270 additions and 180 deletions

View file

@ -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*.
##

View file

@ -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::
##
## <h1><a href="http://force7.de/nimrod">Nimrod</a></h1>
## <h1><a href="http://nimrod-code.org">Nimrod</a></h1>
##
import
@ -88,319 +88,394 @@ proc xmlCheckedTag*(e: PNimrodNode, tag: string,
result = NestList(!"&", result)
macro a*(e: expr): expr =
macro a*(e: expr): expr {.immediate.} =
## generates the HTML ``a`` element.
let e = callsite()
result = xmlCheckedTag(e, "a", "href charset type hreflang rel rev " &
"accesskey tabindex" & commonAttr)
macro acronym*(e: expr): expr =
macro acronym*(e: expr): expr {.immediate.} =
## generates the HTML ``acronym`` element.
let e = callsite()
result = xmlCheckedTag(e, "acronym", commonAttr)
macro address*(e: expr): expr =
macro address*(e: expr): expr {.immediate.} =
## generates the HTML ``address`` element.
let e = callsite()
result = xmlCheckedTag(e, "address", commonAttr)
macro area*(e: expr): expr =
macro area*(e: expr): expr {.immediate.} =
## generates the HTML ``area`` element.
let e = callsite()
result = xmlCheckedTag(e, "area", "shape coords href nohref" &
" accesskey tabindex" & commonAttr, "alt", true)
macro b*(e: expr): expr =
macro b*(e: expr): expr {.immediate.} =
## generates the HTML ``b`` element.
let e = callsite()
result = xmlCheckedTag(e, "b", commonAttr)
macro base*(e: expr): expr =
macro base*(e: expr): expr {.immediate.} =
## generates the HTML ``base`` element.
let e = callsite()
result = xmlCheckedTag(e, "base", "", "href", true)
macro big*(e: expr): expr =
macro big*(e: expr): expr {.immediate.} =
## generates the HTML ``big`` element.
let e = callsite()
result = xmlCheckedTag(e, "big", commonAttr)
macro blockquote*(e: expr): expr =
macro blockquote*(e: expr): expr {.immediate.} =
## generates the HTML ``blockquote`` element.
let e = callsite()
result = xmlCheckedTag(e, "blockquote", " cite" & commonAttr)
macro body*(e: expr): expr =
macro body*(e: expr): expr {.immediate.} =
## generates the HTML ``body`` element.
let e = callsite()
result = xmlCheckedTag(e, "body", commonAttr)
macro br*(e: expr): expr =
macro br*(e: expr): expr {.immediate.} =
## generates the HTML ``br`` element.
let e = callsite()
result = xmlCheckedTag(e, "br", "", "", true)
macro button*(e: expr): expr =
macro button*(e: expr): expr {.immediate.} =
## generates the HTML ``button`` element.
let e = callsite()
result = xmlCheckedTag(e, "button", "accesskey tabindex " &
"disabled name type value" & commonAttr)
macro caption*(e: expr): expr =
macro caption*(e: expr): expr {.immediate.} =
## generates the HTML ``caption`` element.
let e = callsite()
result = xmlCheckedTag(e, "caption", commonAttr)
macro cite*(e: expr): expr =
macro cite*(e: expr): expr {.immediate.} =
## generates the HTML ``cite`` element.
let e = callsite()
result = xmlCheckedTag(e, "cite", commonAttr)
macro code*(e: expr): expr =
macro code*(e: expr): expr {.immediate.} =
## generates the HTML ``code`` element.
let e = callsite()
result = xmlCheckedTag(e, "code", commonAttr)
macro col*(e: expr): expr =
macro col*(e: expr): expr {.immediate.} =
## generates the HTML ``col`` element.
let e = callsite()
result = xmlCheckedTag(e, "col", "span align valign" & commonAttr, "", true)
macro colgroup*(e: expr): expr =
macro colgroup*(e: expr): expr {.immediate.} =
## generates the HTML ``colgroup`` element.
let e = callsite()
result = xmlCheckedTag(e, "colgroup", "span align valign" & commonAttr)
macro dd*(e: expr): expr =
macro dd*(e: expr): expr {.immediate.} =
## generates the HTML ``dd`` element.
let e = callsite()
result = xmlCheckedTag(e, "dd", commonAttr)
macro del*(e: expr): expr =
macro del*(e: expr): expr {.immediate.} =
## generates the HTML ``del`` element.
let e = callsite()
result = xmlCheckedTag(e, "del", "cite datetime" & commonAttr)
macro dfn*(e: expr): expr =
macro dfn*(e: expr): expr {.immediate.} =
## generates the HTML ``dfn`` element.
let e = callsite()
result = xmlCheckedTag(e, "dfn", commonAttr)
macro `div`*(e: expr): expr =
macro `div`*(e: expr): expr {.immediate.} =
## generates the HTML ``div`` element.
let e = callsite()
result = xmlCheckedTag(e, "div", commonAttr)
macro dl*(e: expr): expr =
macro dl*(e: expr): expr {.immediate.} =
## generates the HTML ``dl`` element.
let e = callsite()
result = xmlCheckedTag(e, "dl", commonAttr)
macro dt*(e: expr): expr =
macro dt*(e: expr): expr {.immediate.} =
## generates the HTML ``dt`` element.
let e = callsite()
result = xmlCheckedTag(e, "dt", commonAttr)
macro em*(e: expr): expr =
macro em*(e: expr): expr {.immediate.} =
## generates the HTML ``em`` element.
let e = callsite()
result = xmlCheckedTag(e, "em", commonAttr)
macro fieldset*(e: expr): expr =
macro fieldset*(e: expr): expr {.immediate.} =
## generates the HTML ``fieldset`` element.
let e = callsite()
result = xmlCheckedTag(e, "fieldset", commonAttr)
macro form*(e: expr): expr =
macro form*(e: expr): expr {.immediate.} =
## generates the HTML ``form`` element.
let e = callsite()
result = xmlCheckedTag(e, "form", "method encype accept accept-charset" &
commonAttr, "action")
macro h1*(e: expr): expr =
macro h1*(e: expr): expr {.immediate.} =
## generates the HTML ``h1`` element.
let e = callsite()
result = xmlCheckedTag(e, "h1", commonAttr)
macro h2*(e: expr): expr =
macro h2*(e: expr): expr {.immediate.} =
## generates the HTML ``h2`` element.
let e = callsite()
result = xmlCheckedTag(e, "h2", commonAttr)
macro h3*(e: expr): expr =
macro h3*(e: expr): expr {.immediate.} =
## generates the HTML ``h3`` element.
let e = callsite()
result = xmlCheckedTag(e, "h3", commonAttr)
macro h4*(e: expr): expr =
macro h4*(e: expr): expr {.immediate.} =
## generates the HTML ``h4`` element.
let e = callsite()
result = xmlCheckedTag(e, "h4", commonAttr)
macro h5*(e: expr): expr =
macro h5*(e: expr): expr {.immediate.} =
## generates the HTML ``h5`` element.
let e = callsite()
result = xmlCheckedTag(e, "h5", commonAttr)
macro h6*(e: expr): expr =
macro h6*(e: expr): expr {.immediate.} =
## generates the HTML ``h6`` element.
let e = callsite()
result = xmlCheckedTag(e, "h6", commonAttr)
macro head*(e: expr): expr =
macro head*(e: expr): expr {.immediate.} =
## generates the HTML ``head`` element.
let e = callsite()
result = xmlCheckedTag(e, "head", "profile")
macro html*(e: expr): expr =
macro html*(e: expr): expr {.immediate.} =
## generates the HTML ``html`` element.
let e = callsite()
result = xmlCheckedTag(e, "html", "xmlns", "")
macro hr*(e: expr): expr =
macro hr*(e: expr): expr {.immediate.} =
## generates the HTML ``hr`` element.
let e = callsite()
result = xmlCheckedTag(e, "hr", commonAttr, "", true)
macro i*(e: expr): expr =
macro i*(e: expr): expr {.immediate.} =
## generates the HTML ``i`` element.
let e = callsite()
result = xmlCheckedTag(e, "i", commonAttr)
macro img*(e: expr): expr =
macro img*(e: expr): expr {.immediate.} =
## generates the HTML ``img`` element.
let e = callsite()
result = xmlCheckedTag(e, "img", "longdesc height width", "src alt", true)
macro input*(e: expr): expr =
macro input*(e: expr): expr {.immediate.} =
## generates the HTML ``input`` element.
let e = callsite()
result = xmlCheckedTag(e, "input", "name type value checked maxlength src" &
" alt accept disabled readonly accesskey tabindex" & commonAttr, "", true)
macro ins*(e: expr): expr =
macro ins*(e: expr): expr {.immediate.} =
## generates the HTML ``ins`` element.
let e = callsite()
result = xmlCheckedTag(e, "ins", "cite datetime" & commonAttr)
macro kbd*(e: expr): expr =
macro kbd*(e: expr): expr {.immediate.} =
## generates the HTML ``kbd`` element.
let e = callsite()
result = xmlCheckedTag(e, "kbd", commonAttr)
macro label*(e: expr): expr =
macro label*(e: expr): expr {.immediate.} =
## generates the HTML ``label`` element.
let e = callsite()
result = xmlCheckedTag(e, "label", "for accesskey" & commonAttr)
macro legend*(e: expr): expr =
macro legend*(e: expr): expr {.immediate.} =
## generates the HTML ``legend`` element.
let e = callsite()
result = xmlCheckedTag(e, "legend", "accesskey" & commonAttr)
macro li*(e: expr): expr =
macro li*(e: expr): expr {.immediate.} =
## generates the HTML ``li`` element.
let e = callsite()
result = xmlCheckedTag(e, "li", commonAttr)
macro link*(e: expr): expr =
macro link*(e: expr): expr {.immediate.} =
## generates the HTML ``link`` element.
let e = callsite()
result = xmlCheckedTag(e, "link", "href charset hreflang type rel rev media" &
commonAttr, "", true)
macro map*(e: expr): expr =
macro map*(e: expr): expr {.immediate.} =
## generates the HTML ``map`` element.
let e = callsite()
result = xmlCheckedTag(e, "map", "class title" & eventAttr, "id", false)
macro meta*(e: expr): expr =
macro meta*(e: expr): expr {.immediate.} =
## generates the HTML ``meta`` element.
let e = callsite()
result = xmlCheckedTag(e, "meta", "name http-equiv scheme", "content", true)
macro noscript*(e: expr): expr =
macro noscript*(e: expr): expr {.immediate.} =
## generates the HTML ``noscript`` element.
let e = callsite()
result = xmlCheckedTag(e, "noscript", commonAttr)
macro `object`*(e: expr): expr =
macro `object`*(e: expr): expr {.immediate.} =
## generates the HTML ``object`` element.
let e = callsite()
result = xmlCheckedTag(e, "object", "classid data codebase declare type " &
"codetype archive standby width height name tabindex" & commonAttr)
macro ol*(e: expr): expr =
macro ol*(e: expr): expr {.immediate.} =
## generates the HTML ``ol`` element.
let e = callsite()
result = xmlCheckedTag(e, "ol", commonAttr)
macro optgroup*(e: expr): expr =
macro optgroup*(e: expr): expr {.immediate.} =
## generates the HTML ``optgroup`` element.
let e = callsite()
result = xmlCheckedTag(e, "optgroup", "disabled" & commonAttr, "label", false)
macro option*(e: expr): expr =
macro option*(e: expr): expr {.immediate.} =
## generates the HTML ``option`` element.
let e = callsite()
result = xmlCheckedTag(e, "option", "selected value" & commonAttr)
macro p*(e: expr): expr =
macro p*(e: expr): expr {.immediate.} =
## generates the HTML ``p`` element.
let e = callsite()
result = xmlCheckedTag(e, "p", commonAttr)
macro param*(e: expr): expr =
macro param*(e: expr): expr {.immediate.} =
## generates the HTML ``param`` element.
let e = callsite()
result = xmlCheckedTag(e, "param", "value id type valuetype", "name", true)
macro pre*(e: expr): expr =
macro pre*(e: expr): expr {.immediate.} =
## generates the HTML ``pre`` element.
let e = callsite()
result = xmlCheckedTag(e, "pre", commonAttr)
macro q*(e: expr): expr =
macro q*(e: expr): expr {.immediate.} =
## generates the HTML ``q`` element.
let e = callsite()
result = xmlCheckedTag(e, "q", "cite" & commonAttr)
macro samp*(e: expr): expr =
macro samp*(e: expr): expr {.immediate.} =
## generates the HTML ``samp`` element.
let e = callsite()
result = xmlCheckedTag(e, "samp", commonAttr)
macro script*(e: expr): expr =
macro script*(e: expr): expr {.immediate.} =
## generates the HTML ``script`` element.
let e = callsite()
result = xmlCheckedTag(e, "script", "src charset defer", "type", false)
macro select*(e: expr): expr =
macro select*(e: expr): expr {.immediate.} =
## generates the HTML ``select`` element.
let e = callsite()
result = xmlCheckedTag(e, "select", "name size multiple disabled tabindex" &
commonAttr)
macro small*(e: expr): expr =
macro small*(e: expr): expr {.immediate.} =
## generates the HTML ``small`` element.
let e = callsite()
result = xmlCheckedTag(e, "small", commonAttr)
macro span*(e: expr): expr =
macro span*(e: expr): expr {.immediate.} =
## generates the HTML ``span`` element.
let e = callsite()
result = xmlCheckedTag(e, "span", commonAttr)
macro strong*(e: expr): expr =
macro strong*(e: expr): expr {.immediate.} =
## generates the HTML ``strong`` element.
let e = callsite()
result = xmlCheckedTag(e, "strong", commonAttr)
macro style*(e: expr): expr =
macro style*(e: expr): expr {.immediate.} =
## generates the HTML ``style`` element.
let e = callsite()
result = xmlCheckedTag(e, "style", "media title", "type")
macro sub*(e: expr): expr =
macro sub*(e: expr): expr {.immediate.} =
## generates the HTML ``sub`` element.
let e = callsite()
result = xmlCheckedTag(e, "sub", commonAttr)
macro sup*(e: expr): expr =
macro sup*(e: expr): expr {.immediate.} =
## generates the HTML ``sup`` element.
let e = callsite()
result = xmlCheckedTag(e, "sup", commonAttr)
macro table*(e: expr): expr =
macro table*(e: expr): expr {.immediate.} =
## generates the HTML ``table`` element.
let e = callsite()
result = xmlCheckedTag(e, "table", "summary border cellpadding cellspacing" &
" frame rules width" & commonAttr)
macro tbody*(e: expr): expr =
macro tbody*(e: expr): expr {.immediate.} =
## generates the HTML ``tbody`` element.
let e = callsite()
result = xmlCheckedTag(e, "tbody", "align valign" & commonAttr)
macro td*(e: expr): expr =
macro td*(e: expr): expr {.immediate.} =
## generates the HTML ``td`` element.
let e = callsite()
result = xmlCheckedTag(e, "td", "colspan rowspan abbr axis headers scope" &
" align valign" & commonAttr)
macro textarea*(e: expr): expr =
macro textarea*(e: expr): expr {.immediate.} =
## generates the HTML ``textarea`` element.
let e = callsite()
result = xmlCheckedTag(e, "textarea", " name disabled readonly accesskey" &
" tabindex" & commonAttr, "rows cols", false)
macro tfoot*(e: expr): expr =
macro tfoot*(e: expr): expr {.immediate.} =
## generates the HTML ``tfoot`` element.
let e = callsite()
result = xmlCheckedTag(e, "tfoot", "align valign" & commonAttr)
macro th*(e: expr): expr =
macro th*(e: expr): expr {.immediate.} =
## generates the HTML ``th`` element.
let e = callsite()
result = xmlCheckedTag(e, "th", "colspan rowspan abbr axis headers scope" &
" align valign" & commonAttr)
macro thead*(e: expr): expr =
macro thead*(e: expr): expr {.immediate.} =
## generates the HTML ``thead`` element.
let e = callsite()
result = xmlCheckedTag(e, "thead", "align valign" & commonAttr)
macro title*(e: expr): expr =
macro title*(e: expr): expr {.immediate.} =
## generates the HTML ``title`` element.
let e = callsite()
result = xmlCheckedTag(e, "title")
macro tr*(e: expr): expr =
macro tr*(e: expr): expr {.immediate.} =
## generates the HTML ``tr`` element.
let e = callsite()
result = xmlCheckedTag(e, "tr", "align valign" & commonAttr)
macro tt*(e: expr): expr =
macro tt*(e: expr): expr {.immediate.} =
## generates the HTML ``tt`` element.
let e = callsite()
result = xmlCheckedTag(e, "tt", commonAttr)
macro ul*(e: expr): expr =
macro ul*(e: expr): expr {.immediate.} =
## generates the HTML ``ul`` element.
let e = callsite()
result = xmlCheckedTag(e, "ul", commonAttr)
macro `var`*(e: expr): expr =
macro `var`*(e: expr): expr {.immediate.} =
## generates the HTML ``var`` element.
let e = callsite()
result = xmlCheckedTag(e, "var", commonAttr)
when isMainModule:
var nim = "Nimrod"
echo h1(a(href="http://force7.de/nimrod", nim))
echo h1(a(href="http://nimrod-code.org", nim))

View file

@ -325,8 +325,9 @@ proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color
proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color
macro styledEcho*(m: stmt): stmt =
macro styledEcho*(m: varargs[expr]): stmt =
## to be documented.
let m = callsite()
result = newNimNode(nnkStmtList)
for i in countup(1, m.len - 1):

View file

@ -262,12 +262,13 @@ macro `<>`*(x: expr): expr =
## Constructor macro for XML. Example usage:
##
## .. code-block:: nimrod
## <>a(href="http://force7.de/nimrod", "Nimrod rules.")
## <>a(href="http://nimrod-code.org", "Nimrod rules.")
##
## Produces an XML tree for::
##
## <a href="http://force7.de/nimrod">Nimrod rules.</a>
## <a href="http://nimrod-code.org">Nimrod rules.</a>
##
let x = callsite()
result = xmlConstructor(x)
proc child*(n: PXmlNode, name: string): PXmlNode =