implemented 'bind' for macros

This commit is contained in:
Araq
2012-08-24 01:18:03 +02:00
parent bdf3bee055
commit c7ba6f5eb6
7 changed files with 83 additions and 15 deletions

View File

@@ -439,6 +439,7 @@ type
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
mNGetBoundSym,
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
mInstantiationInfo, mGetTypeInfo

View File

@@ -40,6 +40,7 @@ type
lastException*: PNode
mode*: TEvalMode
globals*: TIdNodeTable # state of global vars
boundSyms: TStrTable # for 'bind' support within macros
PEvalContext* = ref TEvalContext
@@ -65,6 +66,7 @@ proc newEvalContext*(module: PSym, filename: string,
result.module = module
result.mode = mode
initIdNodeTable(result.globals)
initStrTable(result.boundSyms)
proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} =
t.next = c.tos
@@ -931,6 +933,26 @@ proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
"ExpandToAst: expanded symbol is no macro or template")
result = emptyNode
proc getBoundSym(c: PEvalContext, n: PNode): PNode =
# we return either an nkSym or an nkSymChoice; XXX we really need
# to distinguish between open and closed nkSymChoice somehow.
var ident = getIdent(n.strVal)
# semantic checking requires a type; ``fitNode`` deals with it
# appropriately
result = newNodeIT(nkSymChoice, n.info, newType(tyNone, c.module))
var ii: TIdentIter
var a = InitIdentIter(ii, c.boundSyms, ident)
while a != nil:
incl(a.flags, sfUsed)
addSon(result, newSymNode(a, n.info))
a = NextIdentIter(ii, c.boundSyms)
case result.len
of 0: stackTrace(c, n, errUndeclaredIdentifier, n.strVal)
of 1: result = result.sons[0]
else: nil
proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
var m = getMagic(n)
case m
@@ -1151,6 +1173,13 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return
result = copyTree(result)
of mNGetBoundSym:
result = evalAux(c, n.sons[1], {})
if isSpecial(result): return
if not (result.kind in {nkStrLit..nkTripleStrLit}):
stackTrace(c, n, errFieldXNotFound, "getBoundSym")
return
result = getBoundSym(c, result)
of mStrToIdent:
result = evalAux(c, n.sons[1], {})
if isSpecial(result): return
@@ -1240,6 +1269,11 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
if isEmpty(a) or isEmpty(b) or isEmpty(cc): result = emptyNode
else: result = evalOp(m, n, a, b, cc)
proc evalBindStmt(c: PEvalContext, n: PNode) =
for i in 0 .. < n.len:
let a = n.sons[i]
if a.kind == nkSym: StrTableAdd(c.boundSyms, a.sym)
proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
result = emptyNode
dec(gNestedEvals)
@@ -1310,6 +1344,8 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
result.typ = n.typ
of nkPragmaBlock:
result = evalAux(c, n.sons[1], flags)
of nkBindStmt:
evalBindStmt(c, n)
of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt:
result = raiseCannotEval(c, n.info)

View File

@@ -1118,6 +1118,22 @@ proc insertDestructors(c: PContext, varSection: PNode):
return
proc semBindStmtForMacro(c: PContext, n: PNode): PNode =
if c.p.owner.kind != skMacro:
LocalError(n.info, errXNotAllowedHere, "bind")
result = newNodeI(nkBindStmt, n.info)
for i in 0 .. < n.len:
var a = n.sons[i]
let s = QualifiedLookUp(c, a)
if s != nil:
# we need to mark all symbols:
let sc = symChoice(c, a, s)
if sc.kind == nkSym: result.add(sc)
else:
for x in items(sc): result.add(x)
else:
illFormedAst(a)
proc SemStmt(c: PContext, n: PNode): PNode =
const # must be last statements in a block:
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
@@ -1206,6 +1222,8 @@ proc SemStmt(c: PContext, n: PNode): PNode =
result = semPragmaBlock(c, n)
of nkStaticStmt:
result = semStaticStmt(c, n)
of nkBindStmt:
result = semBindStmtForMacro(c, n)
else:
# in interactive mode, we embed the expression in an 'echo':
if gCmd == cmdInteractive:

View File

@@ -91,7 +91,7 @@ type
const
nnkLiterals* = {nnkCharLit..nnkNilLit}
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit}
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
@@ -185,6 +185,10 @@ proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = !i
proc bindSym*(ident: string): PNimrodNode {.magic: "NGetBoundSym".}
## creates a node that binds `ident` to a symbol node. The bound symbol
## needs to be predeclared in a ``bind`` statement!
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
## converts the AST `n` to the concrete Nimrod code and wraps that
@@ -249,6 +253,14 @@ proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
## 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,
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
## produces a new call node. `theProc` is the proc that is called with
## the arguments ``args[0..]``.
result = newNimNode(nnkCall)
result.add(theProc)
result.add(args)
proc newCall*(theProc: TNimrodIdent,
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =

View File

@@ -319,23 +319,22 @@ proc isatty*(f: TFile): bool =
result = isatty(fileHandle(f)) != 0'i32
# XXX:
# These should be private, but there is no yet
# facility for binding local symbols within macros
proc styledEchoProcessArg*(s: string) = write stdout, s
proc styledEchoProcessArg*(style: TStyle) = setStyle({style})
proc styledEchoProcessArg*(style: set[TStyle]) = setStyle style
proc styledEchoProcessArg*(color: TForegroundColor) = setForeGroundColor color
proc styledEchoProcessArg*(color: TBackgroundColor) = setBackGroundColor color
proc styledEchoProcessArg(s: string) = write stdout, s
proc styledEchoProcessArg(style: TStyle) = setStyle({style})
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color
proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color
macro styledEcho*(m: stmt): stmt =
bind styledEchoProcessArg, write, resetAttributes, stdout
result = newNimNode(nnkStmtList)
for i in countup(1, m.len - 1):
result.add(newCall(!"styledEchoProcessArg", m[i]))
result.add(newCall(bindSym"styledEchoProcessArg", m[i]))
result.add(newCall(!"write", newIdentNode("stdout"), newStrLitNode("\n")))
result.add(newCall(!"resetAttributes"))
result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n")))
result.add(newCall(bindSym"resetAttributes"))
when isMainModule:
system.addQuitProc(resetAttributes)
@@ -347,3 +346,5 @@ when isMainModule:
setForeGroundColor(fgBlue)
writeln(stdout, "ordinary text")
styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore})

View File

@@ -1,9 +1,9 @@
version 0.9.0
=============
- make 'bind' default for templates and introduce 'mixin'
- implement 'bind' for macros
- document 'bind' for macros
- ``final`` should be the default for objects
- make 'bind' default for templates and introduce 'mixin'
- implement "closure tuple consists of a single 'ref'" optimization
- implement for loop transformation for first class iterators
@@ -95,7 +95,6 @@ Low priority
- make 'raiseHook' take a closure and provide push and pop for this
--> Lisp-style condition system
- change how comments are part of the AST
- fix & document ``byCopy`` pragma
- ``with proc `+`(x, y: T): T`` for generic code
- new feature: ``distinct T with operations``
- implement the "easy" constructors idea

View File

@@ -157,6 +157,7 @@ Language Additions
in templates.
- Comments can be continued with a backslash continuation character so that
comment pieces don't have to align on the same column.
- Macros support the ``bind`` statement.
2012-02-09 Version 0.8.14 released