summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rwxr-xr-xcompiler/ast.nim43
-rwxr-xr-xcompiler/evals.nim36
-rwxr-xr-xcompiler/parser.nim16
-rwxr-xr-xcompiler/semexprs.nim45
-rwxr-xr-xcompiler/semfold.nim1
-rwxr-xr-xcompiler/semstmts.nim2
6 files changed, 114 insertions, 29 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index bb9803830..85ec2c9b3 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -57,10 +57,10 @@ type
     nkStrLit,             # a string literal ""
     nkRStrLit,            # a raw string literal r""
     nkTripleStrLit,       # a triple string literal """
-    nkMetaNode,           # difficult to explain; represents itself
-                          # (used for macros)
     nkNilLit,             # the nil literal
                           # end of atoms
+    nkMetaNode,           # difficult to explain; represents itself
+                          # (used for macros)
     nkDotCall,            # used to temporarily flag a nkCall node; 
                           # this is used
                           # for transforming ``s.len`` to ``len(s)``
@@ -313,6 +313,7 @@ type
   TMagic* = enum # symbols that require compiler magic:
     mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
     mEcho, mShallowCopy, mSlurp,
+    mAstToYaml, mParseExprToAst, mParseStmtToAst, mExpandMacroToAst,
     mUnaryLt, mSucc, 
     mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, 
     mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, 
@@ -610,6 +611,23 @@ proc copyTree*(src: PNode): PNode
 
 proc discardSons*(father: PNode)
 
+proc len*(n: PNode): int {.inline.} =
+  if isNil(n.sons): result = 0
+  else: result = len(n.sons)
+  
+proc safeLen*(n: PNode): int {.inline.} =
+  ## works even for leaves.
+  if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
+  else: result = len(n.sons)
+  
+proc add*(father, son: PNode) =
+  assert son != nil
+  if isNil(father.sons): father.sons = @[]
+  add(father.sons, son)
+  
+proc `[]`*(n: PNode, i: int): PNode {.inline.} =
+  result = n.sons[i]
+
 var emptyNode* = newNode(nkEmpty)
 # There is a single empty node that is shared! Do not overwrite it!
 
@@ -754,6 +772,10 @@ proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
   result.info = info
   result.typ = typ
 
+proc newMetaNodeIT*(tree: PNode, info: TLineInfo, typ: PType): PNode =
+  result = newNodeIT(nkMetaNode, info, typ)
+  result.add(tree)
+
 proc NewType(kind: TTypeKind, owner: PSym): PType = 
   new(result)
   result.kind = kind
@@ -866,23 +888,6 @@ proc sonsLen(n: PNode): int =
   if isNil(n.sons): result = 0
   else: result = len(n.sons)
   
-proc len*(n: PNode): int {.inline.} =
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
-  
-proc safeLen*(n: PNode): int {.inline.} =
-  ## works even for leaves.
-  if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
-  else: result = len(n.sons)
-  
-proc add*(father, son: PNode) =
-  assert son != nil
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)  
-  
-proc `[]`*(n: PNode, i: int): PNode {.inline.} =
-  result = n.sons[i]
-  
 proc newSons(father: PNode, length: int) = 
   if isNil(father.sons): father.sons = @[]
   setlen(father.sons, len(father.sons) + length)
diff --git a/compiler/evals.nim b/compiler/evals.nim
index 55157ea45..65c64f4d3 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -15,7 +15,8 @@
 
 import 
   strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, 
-  msgs, os, condsyms, idents, renderer, types, passes, semfold, transf
+  msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, 
+  parser, ropes
 
 type 
   PStackFrame* = ref TStackFrame
@@ -751,6 +752,31 @@ proc evalRepr(c: PEvalContext, n: PNode): PNode =
 proc isEmpty(n: PNode): bool = 
   result = (n != nil) and (n.kind == nkEmpty)
 
+# The lexer marks multi-line strings as residing at the line where they are closed
+# This function returns the line where the string begins
+# Maybe the lexer should mark both the beginning and the end of expressions, then
+# this function could be removed
+proc stringStartingLine(s: PNode): int =
+  var totalLines = 0
+  for ln in splitLines(s.strVal): inc totalLines
+
+  result = s.info.line - totalLines
+
+proc evalParseExpr(c: PEvalContext, n: Pnode): Pnode =
+  var code = evalAux(c, n.sons[1], {})
+  var ast = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine)
+
+  if sonsLen(ast) != 1:
+    GlobalError(code.info, errExprExpected, "multiple statements")
+
+  result = ast.sons[0]
+  result.typ = newType(tyExpr, c.module)
+
+proc evalParseStmt(c: PEvalContext, n: Pnode): Pnode =
+  var code = evalAux(c, n.sons[1], {})
+  result = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine)
+  result.typ = newType(tyStmt, c.module)
+
 proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = 
   var m = getMagic(n)
   case m
@@ -775,6 +801,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
   of mAppendStrCh: result = evalAppendStrCh(c, n)
   of mAppendStrStr: result = evalAppendStrStr(c, n)
   of mAppendSeqElem: result = evalAppendSeqElem(c, n)
+  of mParseExprToAst: result = evalParseExpr(c, n)
+  of mParseStmtToAst: result = evalParseStmt(c, n)
   of mNLen: 
     result = evalAux(c, n.sons[1], {efLValue})
     if isSpecial(result): return 
@@ -981,6 +1009,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
     if (a == b) or
         (b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}): 
       result.intVal = 1
+  of mAstToYaml:
+    var ast = evalAux(c, n.sons[1], {efLValue})
+    result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
   of mNHint: 
     result = evalAux(c, n.sons[1], {})
     if isSpecial(result): return 
@@ -1034,6 +1065,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
   dec(gNestedEvals)
   if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations)
   case n.kind                 # atoms:
+  of nkMetaNode:
+    result = copyTree(n.sons[0])
+    result.typ = n.typ
   of nkEmpty: result = n
   of nkSym: result = evalSym(c, n, flags)
   of nkType..nkNilLit: result = copyNode(n) # end of atoms
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 003bd9219..df21c9916 100755
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -30,6 +30,11 @@ proc closeParser*(p: var TParser)
 proc parseTopLevelStmt*(p: var TParser): PNode
   # implements an iterator. Returns the next top-level statement or
   # emtyNode if end of stream.
+
+proc parseString*(s: string, filename: string = "", line: int = 0): PNode
+  # filename and line could be set optionally, when the string originates 
+  # from a certain source file. This way, the compiler could generate
+  # correct error messages referring to the original source.
   
 # helpers for the other parsers
 proc getPrecedence*(tok: TToken): int
@@ -1369,3 +1374,14 @@ proc parseTopLevelStmt(p: var TParser): PNode =
       result = complexOrSimpleStmt(p)
       if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
       break
+
+proc parseString(s: string, filename: string = "", line: int = 0): PNode =
+  var stream = LLStreamOpen(s)
+  stream.lineOffset = line
+
+  var parser : TParser
+  OpenParser(parser, filename, stream)
+
+  result = parser.parseAll
+  CloseParser(parser)
+  
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index f86a4f60d..6dbbba7b8 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -8,6 +8,7 @@
 #
 
 # this module does the semantic checking for expressions
+
 const 
   ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, 
     tyArrayConstr, tyTuple, tySet}
@@ -384,6 +385,10 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
   else: 
     nil
 
+proc isCallExpr(n: PNode): bool = 
+  result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
+                      nkCallStrLit}
+
 proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = 
   if n.kind == nkHiddenDeref: 
     checkSonsLen(n, 1)
@@ -742,7 +747,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     checkSonsLen(n, 2)
     n.sons[0] = makeDeref(n.sons[0])
     # [] operator for tuples requires constant expression:
-    n.sons[1] = semConstExpr(c, n.sons[1])
+    n.sons[1] = semAndEvalConstExpr(c, n.sons[1])
     if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
         {tyInt..tyInt64}: 
       var idx = getOrdValue(n.sons[1])
@@ -883,11 +888,38 @@ proc setMs(n: PNode, s: PSym): PNode =
   n.sons[0] = newSymNode(s)
   n.sons[0].info = n.info
 
+proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
+  result = c.semAndEvalConstExpr(n.sons[i+1])
+
+  if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
+    GlobalError(result.info, errStringLiteralExpected)
+
+proc semExpandMacroToAst(c: PContext, n: PNode, flags: TExprFlags): PNode =
+  if sonsLen(n) == 2:
+    if not isCallExpr(n.sons[1]):
+      GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
+
+    var macroCall = n.sons[1]
+
+    var s = qualifiedLookup(c, macroCall.sons[0], {checkUndeclared})
+    if s == nil:
+      GlobalError(n.info, errUndeclaredIdentifier, macroCall.sons[0].renderTree)
+
+    var expanded : Pnode
+
+    case s.kind
+    of skMacro: expanded = semMacroExpr(c, macroCall, s, false)
+    of skTemplate: expanded = semTemplateExpr(c, macroCall, s, false)
+    else: GlobalError(n.info, errXisNoMacroOrTemplate, s.name.s)
+
+    var macroRetType = newTypeS(s.typ.sons[0].kind, c)
+    result = newMetaNodeIT(expanded, n.info, macroRetType)
+  else:
+    result = semDirectOp(c, n, flags)
+
 proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode = 
   if sonsLen(n) == 2:
-    var a = c.semConstExpr(c, n.sons[1])
-    if a.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: 
-      GlobalError(a.info, errStringLiteralExpected)
+    var a = expectStringArg(c, n, 0)
     try:
       var content = readFile(a.strVal)
       result = newStrNode(nkStrLit, content)
@@ -921,6 +953,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     else:
       result = semDirectOp(c, n, flags)
   of mSlurp: result = semSlurp(c, n, flags)
+  of mExpandMacroToAst: result = semExpandMacroToAst(c, n, flags)
   else: result = semDirectOp(c, n, flags)
 
 proc semIfExpr(c: PContext, n: PNode): PNode = 
@@ -1078,10 +1111,6 @@ proc semBlockExpr(c: PContext, n: PNode): PNode =
   closeScope(c.tab)
   Dec(c.p.nestedBlockCounter)
 
-proc isCallExpr(n: PNode): bool = 
-  result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
-                      nkCallStrLit}
-
 proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode = 
   checkMinSonsLen(n, 2)
   var a: PNode
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 61e63a69f..570656a39 100755
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -208,6 +208,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mNewString, mNewStringOfCap, 
      mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh, 
      mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, 
+     mParseExprToAst, mParseStmtToAst,
      mNLen..mNError, mEqRef: 
     nil
   else: InternalError(a.info, "evalOp(" & $m & ')')
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index ce870a3ad..decc8a2d7 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -19,7 +19,7 @@ proc semWhen(c: PContext, n: PNode): PNode =
     case it.kind
     of nkElifBranch: 
       checkSonsLen(it, 2)
-      var e = semConstBoolExpr(c, it.sons[0])
+      var e = semAndEvalConstExpr(c, it.sons[0])
       if (e.kind != nkIntLit): InternalError(n.info, "semWhen")
       if (e.intVal != 0) and (result == nil): 
         result = semStmt(c, it.sons[1]) # do not open a new scope!