implemented 'bind' for macros

This commit is contained in:
Araq 2012-08-24 01:18:03 +02:00
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, mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
mNGetBoundSym,
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
mInstantiationInfo, mGetTypeInfo mInstantiationInfo, mGetTypeInfo

View file

@ -40,6 +40,7 @@ type
lastException*: PNode lastException*: PNode
mode*: TEvalMode mode*: TEvalMode
globals*: TIdNodeTable # state of global vars globals*: TIdNodeTable # state of global vars
boundSyms: TStrTable # for 'bind' support within macros
PEvalContext* = ref TEvalContext PEvalContext* = ref TEvalContext
@ -65,6 +66,7 @@ proc newEvalContext*(module: PSym, filename: string,
result.module = module result.module = module
result.mode = mode result.mode = mode
initIdNodeTable(result.globals) initIdNodeTable(result.globals)
initStrTable(result.boundSyms)
proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} = proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} =
t.next = c.tos t.next = c.tos
@ -931,6 +933,26 @@ proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
"ExpandToAst: expanded symbol is no macro or template") "ExpandToAst: expanded symbol is no macro or template")
result = emptyNode 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 = proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
var m = getMagic(n) var m = getMagic(n)
case m case m
@ -1151,6 +1173,13 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {efLValue}) result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return if isSpecial(result): return
result = copyTree(result) 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: of mStrToIdent:
result = evalAux(c, n.sons[1], {}) result = evalAux(c, n.sons[1], {})
if isSpecial(result): return 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 if isEmpty(a) or isEmpty(b) or isEmpty(cc): result = emptyNode
else: result = evalOp(m, n, a, b, cc) 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 = proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
result = emptyNode result = emptyNode
dec(gNestedEvals) dec(gNestedEvals)
@ -1310,6 +1344,8 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
result.typ = n.typ result.typ = n.typ
of nkPragmaBlock: of nkPragmaBlock:
result = evalAux(c, n.sons[1], flags) result = evalAux(c, n.sons[1], flags)
of nkBindStmt:
evalBindStmt(c, n)
of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt: nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt:
result = raiseCannotEval(c, n.info) result = raiseCannotEval(c, n.info)

View file

@ -1118,6 +1118,22 @@ proc insertDestructors(c: PContext, varSection: PNode):
return 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 = proc SemStmt(c: PContext, n: PNode): PNode =
const # must be last statements in a block: const # must be last statements in a block:
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
@ -1206,6 +1222,8 @@ proc SemStmt(c: PContext, n: PNode): PNode =
result = semPragmaBlock(c, n) result = semPragmaBlock(c, n)
of nkStaticStmt: of nkStaticStmt:
result = semStaticStmt(c, n) result = semStaticStmt(c, n)
of nkBindStmt:
result = semBindStmtForMacro(c, n)
else: else:
# in interactive mode, we embed the expression in an 'echo': # in interactive mode, we embed the expression in an 'echo':
if gCmd == cmdInteractive: if gCmd == cmdInteractive:

View file

@ -186,6 +186,10 @@ proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
result = newNimNode(nnkIdent) result = newNimNode(nnkIdent)
result.ident = !i 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.} = proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
## converts the AST `n` to the concrete Nimrod code and wraps that ## converts the AST `n` to the concrete Nimrod code and wraps that
## in a string literal node ## in a string literal node
@ -250,6 +254,14 @@ proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
## macros that check its number of arguments. ## macros that check its number of arguments.
if n.len != len: error("macro expects a node with " & $len & " children") 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, proc newCall*(theProc: TNimrodIdent,
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} = args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
## produces a new call node. `theProc` is the proc that is called with ## produces a new call node. `theProc` is the proc that is called with

View file

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

View file

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

View file

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