summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-08-24 01:18:03 +0200
committerAraq <rumpf_a@web.de>2012-08-24 01:18:03 +0200
commitc7ba6f5eb6f555ae650417c6f3bf9cdc0bad1427 (patch)
treee76ddea5dc65e48b16854fa69905f5618998acc1
parentbdf3bee05510718581510cb78fa70ca183039d55 (diff)
downloadNim-c7ba6f5eb6f555ae650417c6f3bf9cdc0bad1427.tar.gz
implemented 'bind' for macros
-rwxr-xr-xcompiler/ast.nim1
-rwxr-xr-xcompiler/evals.nim36
-rwxr-xr-xcompiler/semstmts.nim18
-rwxr-xr-xlib/core/macros.nim14
-rwxr-xr-xlib/pure/terminal.nim23
-rwxr-xr-xtodo.txt5
-rwxr-xr-xweb/news.txt1
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