implemented 'bind' for macros
This commit is contained in:
parent
bdf3bee055
commit
c7ba6f5eb6
7 changed files with 83 additions and 15 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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})
|
||||||
|
|
||||||
5
todo.txt
5
todo.txt
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue