summary refs log tree commit diff stats
path: root/compiler/trees.nim
blob: 2c631af99543910e49b8075914f98d3f8a480f83 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#
#
#           The Nim Compiler
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# tree helper routines

import
  ast, astalgo, lexer, msgs, strutils, wordrecg

proc hasSon(father, son: PNode): bool =
  for i in countup(0, sonsLen(father) - 1):
    if father.sons[i] == son:
      return true
  result = false

proc cyclicTreeAux(n, s: PNode): bool =
  if n == nil:
    return false
  if hasSon(s, n):
    return true
  var m = sonsLen(s)
  addSon(s, n)
  if not (n.kind in {nkEmpty..nkNilLit}):
    for i in countup(0, sonsLen(n) - 1):
      if cyclicTreeAux(n.sons[i], s):
        return true
  result = false
  delSon(s, m)

proc cyclicTree*(n: PNode): bool =
  var s = newNodeI(nkEmpty, n.info)
  result = cyclicTreeAux(n, s)

proc exprStructuralEquivalent*(a, b: PNode): bool =
  result = false
  if a == b:
    result = true
  elif (a != nil) and (b != nil) and (a.kind == b.kind):
    case a.kind
    of nkSym:
      # don't go nuts here: same symbol as string is enough:
      result = a.sym.name.id == b.sym.name.id
    of nkIdent: result = a.ident.id == b.ident.id
    of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
    of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
    of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
    of nkEmpty, nkNilLit, nkType: result = true
    else:
      if sonsLen(a) == sonsLen(b):
        for i in countup(0, sonsLen(a) - 1):
          if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return
        result = true

proc sameTree*(a, b: PNode): bool =
  result = false
  if a == b:
    result = true
  elif a != nil and b != nil and a.kind == b.kind:
    if a.flags != b.flags: return
    if a.info.line != b.info.line: return
    if a.info.col != b.info.col:
      return                  #if a.info.fileIndex <> b.info.fileIndex then exit;
    case a.kind
    of nkSym:
      # don't go nuts here: same symbol as string is enough:
      result = a.sym.name.id == b.sym.name.id
    of nkIdent: result = a.ident.id == b.ident.id
    of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
    of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
    of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
    of nkEmpty, nkNilLit, nkType: result = true
    else:
      if sonsLen(a) == sonsLen(b):
        for i in countup(0, sonsLen(a) - 1):
          if not sameTree(a.sons[i], b.sons[i]): return
        result = true

proc getProcSym*(call: PNode): PSym =
  result = call.sons[0].sym

proc getOpSym*(op: PNode): PSym =
  if op.kind notin {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}:
    result = nil
  else:
    if sonsLen(op) <= 0: internalError(op.info, "getOpSym")
    elif op.sons[0].kind == nkSym: result = op.sons[0].sym
    else: result = nil

proc getMagic*(op: PNode): TMagic =
  case op.kind
  of nkCallKinds:
    case op.sons[0].kind
    of nkSym: result = op.sons[0].sym.magic
    else: result = mNone
  else: result = mNone

proc treeToSym*(t: PNode): PSym =
  result = t.sym

proc isConstExpr*(n: PNode): bool =
  result = (n.kind in
      {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
       nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)

proc isDeepConstExpr*(n: PNode): bool =
  case n.kind
  of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
      nkFloatLit..nkFloat64Lit, nkNilLit:
    result = true
  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
    result = isDeepConstExpr(n.sons[1])
  of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure:
    for i in 0 .. <n.len:
      if not isDeepConstExpr(n.sons[i]): return false
    # XXX once constant objects are supported by the codegen this needs to be
    # weakened:
    result = n.typ.isNil or n.typ.skipTypes({tyGenericInst, tyDistinct}).kind != tyObject
  else: discard

proc flattenTreeAux(d, a: PNode, op: TMagic) =
  if (getMagic(a) == op):     # a is a "leaf", so add it:
    for i in countup(1, sonsLen(a) - 1): # BUGFIX
      flattenTreeAux(d, a.sons[i], op)
  else:
    addSon(d, copyTree(a))

proc flattenTree*(root: PNode, op: TMagic): PNode =
  result = copyNode(root)
  if getMagic(root) == op:
    # BUGFIX: forget to copy prc
    addSon(result, copyNode(root.sons[0]))
    flattenTreeAux(result, root, op)

proc swapOperands*(op: PNode) =
  var tmp = op.sons[1]
  op.sons[1] = op.sons[2]
  op.sons[2] = tmp

proc isRange*(n: PNode): bool {.inline.} =
  if n.kind in nkCallKinds:
    if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or
        n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and
        n[0][1].sym.name.id == ord(wDotDot):
      result = true

proc whichPragma*(n: PNode): TSpecialWord =
  let key = if n.kind == nkExprColonExpr: n.sons[0] else: n
  if key.kind == nkIdent: result = whichKeyword(key.ident)

proc unnestStmts(n, result: PNode) =
  if n.kind == nkStmtList:
    for x in items(n): unnestStmts(x, result)
  elif n.kind notin {nkCommentStmt, nkNilLit}:
    result.add(n)

proc flattenStmts*(n: PNode): PNode =
  ## flattens a nested statement list; used for pattern matching
  result = newNodeI(nkStmtList, n.info)
  unnestStmts(n, result)
  if result.len == 1:
    result = result.sons[0]

proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode =
  result = newNodeI(k, n.info, b-a+1)
  for i in 0 .. b-a: result.sons[i] = n.sons[i+a]