diff options
author | Araq <rumpf_a@web.de> | 2012-08-24 01:18:03 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2012-08-24 01:18:03 +0200 |
commit | c7ba6f5eb6f555ae650417c6f3bf9cdc0bad1427 (patch) | |
tree | e76ddea5dc65e48b16854fa69905f5618998acc1 | |
parent | bdf3bee05510718581510cb78fa70ca183039d55 (diff) | |
download | Nim-c7ba6f5eb6f555ae650417c6f3bf9cdc0bad1427.tar.gz |
implemented 'bind' for macros
-rwxr-xr-x | compiler/ast.nim | 1 | ||||
-rwxr-xr-x | compiler/evals.nim | 36 | ||||
-rwxr-xr-x | compiler/semstmts.nim | 18 | ||||
-rwxr-xr-x | lib/core/macros.nim | 14 | ||||
-rwxr-xr-x | lib/pure/terminal.nim | 23 | ||||
-rwxr-xr-x | todo.txt | 5 | ||||
-rwxr-xr-x | web/news.txt | 1 |
7 files changed, 83 insertions, 15 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 36cafe6f3..bb78fcd57 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/evals.nim b/compiler/evals.nim index 58f797e14..b107015eb 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -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) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b8bf9e970..3bd368b7e 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 1881faf63..d02aba0ca 100755 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -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.} = diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index e57cc2112..5a82586cb 100755 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -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}) + \ No newline at end of file diff --git a/todo.txt b/todo.txt index c251cc5de..254fbf7b7 100755 --- a/todo.txt +++ b/todo.txt @@ -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 diff --git a/web/news.txt b/web/news.txt index ac8b0585b..bbac003f1 100755 --- a/web/news.txt +++ b/web/news.txt @@ -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 |