mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 09:24:36 +00:00
fixes #24258
It uses conditionals to guard against ill formed AST to produce better
error messages, rather than crashing
(cherry picked from commit 8b39b2df7d)
1885 lines
57 KiB
Nim
1885 lines
57 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2013 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# This module implements the renderer of the standard Nim representation.
|
|
|
|
# 'import renderer' is so useful for debugging
|
|
# that Nim shouldn't produce a warning for that:
|
|
{.used.}
|
|
|
|
import
|
|
lexer, options, idents, ast, msgs, lineinfos, wordrecg
|
|
|
|
import std/[strutils]
|
|
|
|
when defined(nimPreviewSlimSystem):
|
|
import std/[syncio, assertions, formatfloat]
|
|
|
|
type
|
|
TRenderFlag* = enum
|
|
renderNone, renderNoBody, renderNoComments, renderDocComments,
|
|
renderNoPragmas, renderIds, renderNoProcDefs, renderSyms, renderRunnableExamples,
|
|
renderIr, renderNonExportedFields, renderExpandUsing, renderNoPostfix
|
|
|
|
TRenderFlags* = set[TRenderFlag]
|
|
TRenderTok* = object
|
|
kind*: TokType
|
|
length*: int16
|
|
sym*: PSym
|
|
|
|
Section = enum
|
|
GenericParams
|
|
ObjectDef
|
|
|
|
TRenderTokSeq* = seq[TRenderTok]
|
|
TSrcGen* = object
|
|
indent*: int
|
|
lineLen*: int
|
|
col: int
|
|
pos*: int # current position for iteration over the buffer
|
|
idx*: int # current token index for iteration over the buffer
|
|
tokens*: TRenderTokSeq
|
|
buf*: string
|
|
pendingNL*: int # negative if not active; else contains the
|
|
# indentation value
|
|
pendingWhitespace: int
|
|
comStack*: seq[PNode] # comment stack
|
|
flags*: TRenderFlags
|
|
inside: set[Section] # Keeps track of contexts we are in
|
|
checkAnon: bool # we're in a context that can contain sfAnon
|
|
inPragma: int
|
|
when defined(nimpretty):
|
|
pendingNewlineCount: int
|
|
fid*: FileIndex
|
|
config*: ConfigRef
|
|
mangler: seq[PSym]
|
|
|
|
proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string
|
|
|
|
# We render the source code in a two phases: The first
|
|
# determines how long the subtree will likely be, the second
|
|
# phase appends to a buffer that will be the output.
|
|
|
|
proc disamb(g: var TSrcGen; s: PSym): int =
|
|
# we group by 's.name.s' to compute the stable name ID.
|
|
result = 0
|
|
for i in 0 ..< g.mangler.len:
|
|
if s == g.mangler[i]: return result
|
|
if s.name.s == g.mangler[i].name.s: inc result
|
|
g.mangler.add s
|
|
|
|
proc isKeyword*(i: PIdent): bool =
|
|
if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and
|
|
(i.id <= ord(tokKeywordHigh) - ord(tkSymbol)):
|
|
result = true
|
|
else:
|
|
result = false
|
|
|
|
proc isExported(n: PNode): bool =
|
|
## Checks if an ident is exported.
|
|
## This is meant to be used with idents in nkIdentDefs.
|
|
case n.kind
|
|
of nkPostfix:
|
|
n[0].ident.s == "*" and n[1].kind == nkIdent
|
|
of nkPragmaExpr:
|
|
n[0].isExported()
|
|
else: false
|
|
|
|
proc renderDefinitionName*(s: PSym, noQuotes = false): string =
|
|
## Returns the definition name of the symbol.
|
|
##
|
|
## If noQuotes is false the symbol may be returned in backticks. This will
|
|
## happen if the name happens to be a keyword or the first character is not
|
|
## part of the SymStartChars set.
|
|
let x = s.name.s
|
|
if noQuotes or (x[0] in SymStartChars and not renderer.isKeyword(s.name)):
|
|
result = x
|
|
else:
|
|
result = '`' & x & '`'
|
|
|
|
template inside(g: var TSrcGen, section: Section, body: untyped) =
|
|
## Runs `body` with `section` included in `g.inside`.
|
|
## Removes it at the end of the body if `g` wasn't inside it
|
|
## before the template.
|
|
let wasntInSection = section notin g.inside
|
|
g.inside.incl section
|
|
body
|
|
if wasntInSection:
|
|
g.inside.excl section
|
|
|
|
template outside(g: var TSrcGen, section: Section, body: untyped) =
|
|
## Temporarily removes `section` from `g.inside`. Adds it back
|
|
## at the end of the body if `g` was inside it before the template
|
|
let wasInSection = section in g.inside
|
|
g.inside.excl section
|
|
body
|
|
if wasInSection:
|
|
g.inside.incl section
|
|
|
|
const
|
|
IndentWidth = 2
|
|
longIndentWid = IndentWidth * 2
|
|
MaxLineLen = 80
|
|
LineCommentColumn = 30
|
|
|
|
when defined(nimpretty):
|
|
proc minmaxLine(n: PNode): (int, int) =
|
|
case n.kind
|
|
of nkTripleStrLit:
|
|
result = (n.info.line.int, n.info.line.int + countLines(n.strVal))
|
|
of nkCommentStmt:
|
|
result = (n.info.line.int, n.info.line.int + countLines(n.comment))
|
|
else:
|
|
result = (n.info.line.int, n.info.line.int)
|
|
for i in 0..<n.safeLen:
|
|
let (currMin, currMax) = minmaxLine(n[i])
|
|
if currMin < result[0]: result[0] = currMin
|
|
if currMax > result[1]: result[1] = currMax
|
|
|
|
proc lineDiff(a, b: PNode): int =
|
|
result = minmaxLine(b)[0] - minmaxLine(a)[1]
|
|
|
|
proc initSrcGen(renderFlags: TRenderFlags; config: ConfigRef): TSrcGen =
|
|
result = TSrcGen(comStack: @[], tokens: @[], indent: 0,
|
|
lineLen: 0, pos: 0, idx: 0, buf: "",
|
|
flags: renderFlags, pendingNL: -1,
|
|
pendingWhitespace: -1, inside: {},
|
|
config: config
|
|
)
|
|
|
|
proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
|
|
g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym)
|
|
g.buf.add(s)
|
|
if kind != tkSpaces:
|
|
inc g.col, s.len
|
|
|
|
proc addPendingNL(g: var TSrcGen) =
|
|
if g.pendingNL >= 0:
|
|
when defined(nimpretty):
|
|
let newlines = repeat("\n", clamp(g.pendingNewlineCount, 1, 3))
|
|
else:
|
|
const newlines = "\n"
|
|
addTok(g, tkSpaces, newlines & spaces(g.pendingNL))
|
|
g.lineLen = g.pendingNL
|
|
g.col = g.pendingNL
|
|
g.pendingNL = - 1
|
|
g.pendingWhitespace = -1
|
|
elif g.pendingWhitespace >= 0:
|
|
addTok(g, tkSpaces, spaces(g.pendingWhitespace))
|
|
g.pendingWhitespace = -1
|
|
|
|
proc putNL(g: var TSrcGen, indent: int) =
|
|
if g.pendingNL >= 0: addPendingNL(g)
|
|
else:
|
|
addTok(g, tkSpaces, "\n")
|
|
g.col = 0
|
|
|
|
g.pendingNL = indent
|
|
g.lineLen = indent
|
|
g.pendingWhitespace = -1
|
|
|
|
proc previousNL(g: TSrcGen): bool =
|
|
result = g.pendingNL >= 0 or (g.tokens.len > 0 and
|
|
g.tokens[^1].kind == tkSpaces)
|
|
|
|
proc putNL(g: var TSrcGen) =
|
|
putNL(g, g.indent)
|
|
|
|
proc optNL(g: var TSrcGen, indent: int) =
|
|
g.pendingNL = indent
|
|
g.lineLen = indent
|
|
g.col = g.indent
|
|
when defined(nimpretty): g.pendingNewlineCount = 0
|
|
|
|
proc optNL(g: var TSrcGen) =
|
|
optNL(g, g.indent)
|
|
|
|
proc optNL(g: var TSrcGen; a, b: PNode) =
|
|
g.pendingNL = g.indent
|
|
g.lineLen = g.indent
|
|
g.col = g.indent
|
|
when defined(nimpretty): g.pendingNewlineCount = lineDiff(a, b)
|
|
|
|
proc indentNL(g: var TSrcGen) =
|
|
inc(g.indent, IndentWidth)
|
|
g.pendingNL = g.indent
|
|
g.lineLen = g.indent
|
|
|
|
proc dedent(g: var TSrcGen) =
|
|
dec(g.indent, IndentWidth)
|
|
assert(g.indent >= 0)
|
|
if g.pendingNL > IndentWidth:
|
|
dec(g.pendingNL, IndentWidth)
|
|
dec(g.lineLen, IndentWidth)
|
|
|
|
proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
|
|
if kind != tkSpaces:
|
|
addPendingNL(g)
|
|
if s.len > 0 or kind in {tkHideableStart, tkHideableEnd}:
|
|
addTok(g, kind, s, sym)
|
|
else:
|
|
g.pendingWhitespace = s.len
|
|
inc g.col, s.len
|
|
inc(g.lineLen, s.len)
|
|
|
|
proc putComment(g: var TSrcGen, s: string) =
|
|
if s.len == 0: return
|
|
var i = 0
|
|
let hi = s.len - 1
|
|
let isCode = (s.len >= 2) and (s[1] != ' ')
|
|
let ind = g.col
|
|
var com = "## "
|
|
while i <= hi:
|
|
case s[i]
|
|
of '\0':
|
|
break
|
|
of '\r':
|
|
put(g, tkComment, com)
|
|
com = "## "
|
|
inc(i)
|
|
if i <= hi and s[i] == '\n': inc(i)
|
|
optNL(g, ind)
|
|
of '\n':
|
|
put(g, tkComment, com)
|
|
com = "## "
|
|
inc(i)
|
|
optNL(g, ind)
|
|
of ' ', '\t':
|
|
com.add(s[i])
|
|
inc(i)
|
|
else:
|
|
# we may break the comment into a multi-line comment if the line
|
|
# gets too long:
|
|
# compute length of the following word:
|
|
var j = i
|
|
while j <= hi and s[j] > ' ': inc(j)
|
|
if not isCode and (g.col + (j - i) > MaxLineLen):
|
|
put(g, tkComment, com)
|
|
optNL(g, ind)
|
|
com = "## "
|
|
while i <= hi and s[i] > ' ':
|
|
com.add(s[i])
|
|
inc(i)
|
|
put(g, tkComment, com)
|
|
optNL(g)
|
|
|
|
proc maxLineLength(s: string): int =
|
|
result = 0
|
|
if s.len == 0: return 0
|
|
var i = 0
|
|
let hi = s.len - 1
|
|
var lineLen = 0
|
|
while i <= hi:
|
|
case s[i]
|
|
of '\0':
|
|
break
|
|
of '\r':
|
|
inc(i)
|
|
if i <= hi and s[i] == '\n': inc(i)
|
|
result = max(result, lineLen)
|
|
lineLen = 0
|
|
of '\n':
|
|
inc(i)
|
|
result = max(result, lineLen)
|
|
lineLen = 0
|
|
else:
|
|
inc(lineLen)
|
|
inc(i)
|
|
|
|
proc putRawStr(g: var TSrcGen, kind: TokType, s: string) =
|
|
var i = 0
|
|
let hi = s.len - 1
|
|
var str = ""
|
|
while i <= hi:
|
|
case s[i]
|
|
of '\r':
|
|
put(g, kind, str)
|
|
str = ""
|
|
inc(i)
|
|
if i <= hi and s[i] == '\n': inc(i)
|
|
optNL(g, 0)
|
|
of '\n':
|
|
put(g, kind, str)
|
|
str = ""
|
|
inc(i)
|
|
optNL(g, 0)
|
|
else:
|
|
str.add(s[i])
|
|
inc(i)
|
|
put(g, kind, str)
|
|
|
|
proc containsNL(s: string): bool =
|
|
for i in 0..<s.len:
|
|
case s[i]
|
|
of '\r', '\n':
|
|
return true
|
|
else:
|
|
discard
|
|
result = false
|
|
|
|
proc pushCom(g: var TSrcGen, n: PNode) =
|
|
setLen(g.comStack, g.comStack.len + 1)
|
|
g.comStack[^1] = n
|
|
|
|
proc popAllComs(g: var TSrcGen) =
|
|
setLen(g.comStack, 0)
|
|
|
|
const
|
|
Space = " "
|
|
|
|
proc shouldRenderComment(g: TSrcGen): bool {.inline.} =
|
|
(renderNoComments notin g.flags or renderDocComments in g.flags)
|
|
|
|
proc shouldRenderComment(g: TSrcGen, n: PNode): bool {.inline.} =
|
|
shouldRenderComment(g) and n.comment.len > 0
|
|
|
|
proc gcom(g: var TSrcGen, n: PNode) =
|
|
assert(n != nil)
|
|
if shouldRenderComment(g, n):
|
|
var oneSpaceAdded = 0
|
|
if (g.pendingNL < 0) and (g.buf.len > 0) and (g.buf[^1] != ' '):
|
|
put(g, tkSpaces, Space)
|
|
oneSpaceAdded = 1
|
|
# Before long comments we cannot make sure that a newline is generated,
|
|
# because this might be wrong. But it is no problem in practice.
|
|
if (g.pendingNL < 0) and (g.buf.len > 0) and
|
|
(g.col < LineCommentColumn):
|
|
var ml = maxLineLength(n.comment)
|
|
if ml + LineCommentColumn <= MaxLineLen:
|
|
put(g, tkSpaces, spaces(LineCommentColumn - g.col))
|
|
dec g.col, oneSpaceAdded
|
|
putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n);
|
|
|
|
proc gcoms(g: var TSrcGen) =
|
|
for i in 0..high(g.comStack): gcom(g, g.comStack[i])
|
|
popAllComs(g)
|
|
|
|
proc lsub(g: TSrcGen; n: PNode): int
|
|
proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
|
|
proc skip(t: PType): PType =
|
|
result = t
|
|
while result != nil and result.kind in {tyGenericInst, tyRange, tyVar,
|
|
tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}:
|
|
result = skipModifier(result)
|
|
|
|
result = ""
|
|
let typ = n.typ.skip
|
|
if typ != nil and typ.kind in {tyBool, tyEnum}:
|
|
if sfPure in typ.sym.flags:
|
|
result = typ.sym.name.s & '.'
|
|
let enumfields = typ.n
|
|
# we need a slow linear search because of enums with holes:
|
|
for e in items(enumfields):
|
|
if e.sym.position == x:
|
|
result &= e.sym.name.s
|
|
return
|
|
|
|
if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
|
|
elif nfBase8 in n.flags:
|
|
var y = if size < sizeof(BiggestInt): x and ((1.BiggestInt shl (size*8)) - 1)
|
|
else: x
|
|
result = "0o" & toOct(y, size * 3)
|
|
elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
|
|
else: result = $x
|
|
|
|
proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
|
|
if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
|
|
elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
|
|
elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
|
|
else: result = $cast[BiggestUInt](x)
|
|
|
|
proc atom(g: TSrcGen; n: PNode): string =
|
|
when defined(nimpretty):
|
|
doAssert g.config != nil, "g.config not initialized!"
|
|
let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
|
|
" " & fileSection(g.config, g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
|
|
else:
|
|
""
|
|
if n.info.offsetA <= n.info.offsetB:
|
|
# for some constructed tokens this can not be the case and we're better
|
|
# off to not mess with the offset then.
|
|
return fileSection(g.config, g.fid, n.info.offsetA, n.info.offsetB) & comment
|
|
var f: float32
|
|
case n.kind
|
|
of nkEmpty: result = ""
|
|
of nkIdent: result = n.ident.s
|
|
of nkSym: result = n.sym.name.s
|
|
of nkClosedSymChoice, nkOpenSymChoice: result = n[0].sym.name.s
|
|
of nkStrLit: result = ""; result.addQuoted(n.strVal)
|
|
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
|
|
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
|
|
of nkCharLit:
|
|
result = "\'"
|
|
result.addEscapedChar(chr(int(n.intVal)));
|
|
result.add '\''
|
|
of nkIntLit: result = litAux(g, n, n.intVal, 4)
|
|
of nkInt8Lit: result = litAux(g, n, n.intVal, 1) & "\'i8"
|
|
of nkInt16Lit: result = litAux(g, n, n.intVal, 2) & "\'i16"
|
|
of nkInt32Lit: result = litAux(g, n, n.intVal, 4) & "\'i32"
|
|
of nkInt64Lit: result = litAux(g, n, n.intVal, 8) & "\'i64"
|
|
of nkUIntLit: result = ulitAux(g, n, n.intVal, 4) & "\'u"
|
|
of nkUInt8Lit: result = ulitAux(g, n, n.intVal, 1) & "\'u8"
|
|
of nkUInt16Lit: result = ulitAux(g, n, n.intVal, 2) & "\'u16"
|
|
of nkUInt32Lit: result = ulitAux(g, n, n.intVal, 4) & "\'u32"
|
|
of nkUInt64Lit: result = ulitAux(g, n, n.intVal, 8) & "\'u64"
|
|
of nkFloatLit:
|
|
if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal)
|
|
else: result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[] , 8)
|
|
of nkFloat32Lit:
|
|
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
|
|
result = $n.floatVal & "\'f32"
|
|
else:
|
|
f = n.floatVal.float32
|
|
result = litAux(g, n, (cast[ptr int32](addr(f)))[], 4) & "\'f32"
|
|
of nkFloat64Lit:
|
|
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
|
|
result = $n.floatVal & "\'f64"
|
|
else:
|
|
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
|
|
of nkFloat128Lit:
|
|
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
|
|
result = $n.floatVal & "\'f128"
|
|
else:
|
|
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
|
|
of nkNilLit: result = "nil"
|
|
of nkType:
|
|
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
|
|
else: result = "[type node]"
|
|
else:
|
|
internalError(g.config, "renderer.atom " & $n.kind)
|
|
result = ""
|
|
|
|
proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
|
|
assert(theEnd < 0)
|
|
result = 0
|
|
for i in start..n.len + theEnd:
|
|
let param = n[i]
|
|
if nfDefaultParam notin param.flags:
|
|
inc(result, lsub(g, param))
|
|
inc(result, 2) # for ``, ``
|
|
if result > 0:
|
|
dec(result, 2) # last does not get a comma!
|
|
|
|
proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
|
|
assert(theEnd < 0)
|
|
result = 0
|
|
for i in start..n.len + theEnd: inc(result, lsub(g, n[i]))
|
|
|
|
proc origUsingType(n: PNode): PSym {.inline.} =
|
|
## Returns the type that a parameter references. Check with referencesUsing first
|
|
## to check `n` is actually referencing a using node
|
|
# If the node is untyped the typ field will be nil
|
|
if n[0].sym.typ != nil:
|
|
n[0].sym.typ.sym
|
|
else: nil
|
|
|
|
proc referencesUsing(n: PNode): bool =
|
|
## Returns true if n references a using statement.
|
|
## e.g. proc foo(x) # x doesn't have type or def value so it references a using
|
|
result = n.kind == nkIdentDefs and
|
|
# Sometimes the node might not have been semmed (e.g. doc0) and will be nkIdent instead
|
|
n[0].kind == nkSym and
|
|
# Templates/macros can have parameters with no type (But their orig type will be nil)
|
|
n.origUsingType != nil and
|
|
n[1].kind == nkEmpty and n[2].kind == nkEmpty
|
|
|
|
proc lsub(g: TSrcGen; n: PNode): int =
|
|
# computes the length of a tree
|
|
result = 0
|
|
if isNil(n): return 0
|
|
if shouldRenderComment(g, n): return MaxLineLen + 1
|
|
case n.kind
|
|
of nkEmpty: result = 0
|
|
of nkTripleStrLit:
|
|
if containsNL(n.strVal): result = MaxLineLen + 1
|
|
else: result = atom(g, n).len
|
|
of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit:
|
|
result = atom(g, n).len
|
|
of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern, nkObjConstr:
|
|
result = lsub(g, n[0]) + lcomma(g, n, 1) + 2
|
|
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(g, n[1])
|
|
of nkCast: result = lsub(g, n[0]) + lsub(g, n[1]) + len("cast[]()")
|
|
of nkAddr: result = (if n.len>0: lsub(g, n[0]) + len("addr()") else: 4)
|
|
of nkStaticExpr: result = lsub(g, n[0]) + len("static_")
|
|
of nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString: result = lsub(g, n[0])
|
|
of nkCommand: result = lsub(g, n[0]) + lcomma(g, n, 1) + 1
|
|
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3
|
|
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2
|
|
of nkTupleConstr:
|
|
# assume the trailing comma:
|
|
result = lcomma(g, n) + 3
|
|
of nkArgList: result = lcomma(g, n)
|
|
of nkTableConstr:
|
|
result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
|
|
of nkClosedSymChoice, nkOpenSymChoice:
|
|
if n.len > 0: result += lsub(g, n[0])
|
|
of nkOpenSym: result = lsub(g, n[0])
|
|
of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
|
|
of nkTupleClassTy: result = len("tuple")
|
|
of nkDotExpr: result = lsons(g, n) + 1
|
|
of nkBind: result = lsons(g, n) + len("bind_")
|
|
of nkBindStmt: result = lcomma(g, n) + len("bind_")
|
|
of nkMixinStmt: result = lcomma(g, n) + len("mixin_")
|
|
of nkCheckedFieldExpr: result = lsub(g, n[0])
|
|
of nkLambda: result = lsons(g, n) + len("proc__=_")
|
|
of nkDo: result = lsons(g, n) + len("do__:_")
|
|
of nkConstDef, nkIdentDefs:
|
|
result = lcomma(g, n, 0, - 3)
|
|
if n.referencesUsing:
|
|
result += lsub(g, newSymNode(n.origUsingType)) + 2
|
|
else:
|
|
if n[^2].kind != nkEmpty: result += lsub(g, n[^2]) + 2
|
|
if n[^1].kind != nkEmpty: result += lsub(g, n[^1]) + 3
|
|
of nkVarTuple:
|
|
if n[^1].kind == nkEmpty:
|
|
result = lcomma(g, n, 0, - 2) + len("()")
|
|
else:
|
|
result = lcomma(g, n, 0, - 3) + len("() = ") + lsub(g, lastSon(n))
|
|
of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(g, n)
|
|
of nkChckRange64: result = len("chckRange64") + 2 + lcomma(g, n)
|
|
of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n)
|
|
of nkObjDownConv, nkObjUpConv:
|
|
result = 2
|
|
if n.len >= 1: result += lsub(g, n[0])
|
|
result += lcomma(g, n, 1)
|
|
of nkExprColonExpr: result = lsons(g, n) + 2
|
|
of nkInfix: result = lsons(g, n) + 2
|
|
of nkPrefix:
|
|
result = lsons(g, n)+1+(if n.len > 0 and n[1].kind == nkInfix: 2 else: 0)
|
|
of nkPostfix:
|
|
if renderNoPostfix notin g.flags:
|
|
result = lsons(g, n)
|
|
else:
|
|
result = lsub(g, n[1])
|
|
of nkCallStrLit: result = lsons(g, n)
|
|
of nkPragmaExpr: result = lsub(g, n[0]) + lcomma(g, n, 1)
|
|
of nkRange: result = lsons(g, n) + 2
|
|
of nkDerefExpr: result = lsub(g, n[0]) + 2
|
|
of nkAccQuoted: result = lsons(g, n) + 2
|
|
of nkIfExpr:
|
|
result = lsub(g, n[0][0]) + lsub(g, n[0][1]) + lsons(g, n, 1) +
|
|
len("if_:_")
|
|
of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
|
|
of nkElseExpr: result = lsub(g, n[0]) + len("_else:_") # type descriptions
|
|
of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()")
|
|
of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref")
|
|
of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr")
|
|
of nkVarTy, nkOutTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var")
|
|
of nkDistinctTy:
|
|
result = len("distinct") + (if n.len > 0: lsub(g, n[0])+1 else: 0)
|
|
if n.len > 1:
|
|
result += (if n[1].kind == nkWith: len("_with_") else: len("_without_"))
|
|
result += lcomma(g, n[1])
|
|
of nkStaticTy: result = (if n.len > 0: lsub(g, n[0]) else: 0) +
|
|
len("static[]")
|
|
of nkTypeDef: result = lsons(g, n) + 3
|
|
of nkOfInherit: result = lsub(g, n[0]) + len("of_")
|
|
of nkProcTy: result = lsons(g, n) + len("proc_")
|
|
of nkIteratorTy: result = lsons(g, n) + len("iterator_")
|
|
of nkSinkAsgn: result = lsons(g, n) + len("`=sink`(, )")
|
|
of nkEnumTy:
|
|
if n.len > 0:
|
|
result = lsub(g, n[0]) + lcomma(g, n, 1) + len("enum_")
|
|
else:
|
|
result = len("enum")
|
|
of nkEnumFieldDef: result = lsons(g, n) + 3
|
|
of nkVarSection, nkLetSection:
|
|
if n.len > 1: result = MaxLineLen + 1
|
|
else: result = lsons(g, n) + len("var_")
|
|
of nkUsingStmt:
|
|
if n.len > 1: result = MaxLineLen + 1
|
|
else: result = lsons(g, n) + len("using_")
|
|
of nkReturnStmt:
|
|
if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags:
|
|
result = len("return_") + lsub(g, n[0][1])
|
|
else:
|
|
result = len("return_") + lsub(g, n[0])
|
|
of nkRaiseStmt: result = lsub(g, n[0]) + len("raise_")
|
|
of nkYieldStmt: result = lsub(g, n[0]) + len("yield_")
|
|
of nkDiscardStmt: result = lsub(g, n[0]) + len("discard_")
|
|
of nkBreakStmt: result = lsub(g, n[0]) + len("break_")
|
|
of nkContinueStmt: result = lsub(g, n[0]) + len("continue_")
|
|
of nkPragma: result = lcomma(g, n) + 4
|
|
of nkCommentStmt: result = n.comment.len
|
|
of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
|
|
of nkImportAs: result = lsub(g, n[0]) + len("_as_") + lsub(g, n[1])
|
|
of nkElifBranch: result = lsons(g, n) + len("elif_:_")
|
|
of nkElse: result = lsub(g, n[0]) + len("else:_")
|
|
of nkFinally: result = lsub(g, n[0]) + len("finally:_")
|
|
of nkGenericParams: result = lcomma(g, n) + 2
|
|
of nkFormalParams:
|
|
result = lcomma(g, n, 1) + 2
|
|
if n[0].kind != nkEmpty: result += lsub(g, n[0]) + 2
|
|
of nkExceptBranch:
|
|
result = lcomma(g, n, 0, -2) + lsub(g, lastSon(n)) + len("except_:_")
|
|
of nkObjectTy:
|
|
result = len("object_")
|
|
else: result = MaxLineLen + 1
|
|
|
|
proc fits(g: TSrcGen, x: int): bool =
|
|
result = x <= MaxLineLen
|
|
|
|
type
|
|
TSubFlag = enum
|
|
rfLongMode, rfInConstExpr
|
|
TSubFlags = set[TSubFlag]
|
|
TContext = tuple[spacing: int, flags: TSubFlags]
|
|
|
|
const
|
|
emptyContext: TContext = (spacing: 0, flags: {})
|
|
|
|
proc initContext(): TContext =
|
|
result = (spacing: 0, flags: {})
|
|
|
|
proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false)
|
|
proc gsub(g: var TSrcGen, n: PNode, fromStmtList = false) =
|
|
var c: TContext = initContext()
|
|
gsub(g, n, c, fromStmtList = fromStmtList)
|
|
|
|
proc hasCom(n: PNode): bool =
|
|
result = false
|
|
if n.isNil: return false
|
|
if n.comment.len > 0: return true
|
|
case n.kind
|
|
of nkEmpty..nkNilLit: discard
|
|
else:
|
|
for i in 0..<n.len:
|
|
if hasCom(n[i]): return true
|
|
|
|
proc putWithSpace(g: var TSrcGen, kind: TokType, s: string) =
|
|
put(g, kind, s)
|
|
put(g, tkSpaces, Space)
|
|
|
|
proc isHideable(config: ConfigRef, n: PNode): bool =
|
|
# xxx compare `ident` directly with `getIdent(cache, wRaises)`, but
|
|
# this requires a `cache`.
|
|
case n.kind
|
|
of nkExprColonExpr:
|
|
result = n[0].kind == nkIdent and
|
|
n[0].ident.s.nimIdentNormalize in ["raises", "tags", "extern", "deprecated", "forbids", "stacktrace"]
|
|
of nkIdent: result = n.ident.s in ["gcsafe", "deprecated"]
|
|
else: result = false
|
|
|
|
proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
|
|
theEnd: int = - 1, separator = tkComma) =
|
|
let inPragma = g.inPragma == 1 # just the top-level
|
|
var inHideable = false
|
|
for i in start..n.len + theEnd:
|
|
let c = i < n.len + theEnd
|
|
let sublen = lsub(g, n[i]) + ord(c)
|
|
if not fits(g, g.lineLen + sublen) and (ind + sublen < MaxLineLen): optNL(g, ind)
|
|
let oldLen = g.tokens.len
|
|
if inPragma:
|
|
if not inHideable and isHideable(g.config, n[i]):
|
|
inHideable = true
|
|
put(g, tkHideableStart, "")
|
|
elif inHideable and not isHideable(g.config, n[i]):
|
|
inHideable = false
|
|
put(g, tkHideableEnd, "")
|
|
gsub(g, n[i])
|
|
if c:
|
|
if g.tokens.len > oldLen:
|
|
putWithSpace(g, separator, $separator)
|
|
if shouldRenderComment(g) and hasCom(n[i]):
|
|
gcoms(g)
|
|
optNL(g, ind)
|
|
if inHideable:
|
|
put(g, tkHideableEnd, "")
|
|
inHideable = false
|
|
|
|
proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
|
|
theEnd: int = -1) =
|
|
var ind: int
|
|
if rfInConstExpr in c.flags:
|
|
ind = g.indent + IndentWidth
|
|
else:
|
|
ind = g.lineLen
|
|
if ind > MaxLineLen div 2: ind = g.indent + longIndentWid
|
|
gcommaAux(g, n, ind, start, theEnd)
|
|
|
|
proc gcomma(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) =
|
|
var ind = g.lineLen
|
|
if ind > MaxLineLen div 2: ind = g.indent + longIndentWid
|
|
gcommaAux(g, n, ind, start, theEnd)
|
|
|
|
proc gsemicolon(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) =
|
|
var ind = g.lineLen
|
|
if ind > MaxLineLen div 2: ind = g.indent + longIndentWid
|
|
gcommaAux(g, n, ind, start, theEnd, tkSemiColon)
|
|
|
|
proc gsons(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
|
|
theEnd: int = - 1) =
|
|
for i in start..n.len + theEnd: gsub(g, n[i], c)
|
|
|
|
proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TokType,
|
|
k: string) =
|
|
if n.len == 0: return # empty var sections are possible
|
|
putWithSpace(g, kind, k)
|
|
gcoms(g)
|
|
indentNL(g)
|
|
for i in 0..<n.len:
|
|
optNL(g)
|
|
gsub(g, n[i], c)
|
|
gcoms(g)
|
|
dedent(g)
|
|
|
|
proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool =
|
|
result = shouldRenderComment(g, n)
|
|
if not result:
|
|
# check further
|
|
for i in start..n.len + theEnd:
|
|
if (lsub(g, n[i]) > MaxLineLen):
|
|
result = true
|
|
break
|
|
|
|
proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
|
|
if n.kind == nkEmpty: return
|
|
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
|
|
if doIndent: indentNL(g)
|
|
for i in 0..<n.len:
|
|
if i > 0:
|
|
optNL(g, n[i-1], n[i])
|
|
else:
|
|
optNL(g)
|
|
if n[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
|
|
gstmts(g, n[i], c, doIndent=false)
|
|
else:
|
|
gsub(g, n[i], fromStmtList = true)
|
|
gcoms(g)
|
|
if doIndent: dedent(g)
|
|
else:
|
|
indentNL(g)
|
|
gsub(g, n)
|
|
gcoms(g)
|
|
dedent(g)
|
|
optNL(g)
|
|
|
|
|
|
proc gcond(g: var TSrcGen, n: PNode) =
|
|
if n.kind == nkStmtListExpr:
|
|
put(g, tkParLe, "(")
|
|
gsub(g, n)
|
|
if n.kind == nkStmtListExpr:
|
|
put(g, tkParRi, ")")
|
|
|
|
proc gif(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
gcond(g, n[0][0])
|
|
putWithSpace(g, tkColon, ":")
|
|
if longMode(g, n) or (lsub(g, n[0][1]) + g.lineLen > MaxLineLen):
|
|
incl(c.flags, rfLongMode)
|
|
gcoms(g) # a good place for comments
|
|
gstmts(g, n[0][1], c)
|
|
for i in 1..<n.len:
|
|
optNL(g)
|
|
gsub(g, n[i], c)
|
|
|
|
proc gwhile(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
putWithSpace(g, tkWhile, "while")
|
|
gcond(g, n[0])
|
|
putWithSpace(g, tkColon, ":")
|
|
if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
|
|
incl(c.flags, rfLongMode)
|
|
gcoms(g) # a good place for comments
|
|
gstmts(g, n[1], c)
|
|
|
|
proc gpattern(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
put(g, tkCurlyLe, "{")
|
|
if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
|
|
incl(c.flags, rfLongMode)
|
|
gcoms(g) # a good place for comments
|
|
gstmts(g, n, c)
|
|
put(g, tkCurlyRi, "}")
|
|
|
|
proc gpragmaBlock(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
gsub(g, n[0])
|
|
putWithSpace(g, tkColon, ":")
|
|
if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
|
|
incl(c.flags, rfLongMode)
|
|
gcoms(g) # a good place for comments
|
|
gstmts(g, n[1], c)
|
|
|
|
proc gtry(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
put(g, tkTry, "try")
|
|
putWithSpace(g, tkColon, ":")
|
|
if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
|
|
incl(c.flags, rfLongMode)
|
|
gcoms(g) # a good place for comments
|
|
gstmts(g, n[0], c)
|
|
gsons(g, n, c, 1)
|
|
|
|
proc gfor(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
putWithSpace(g, tkFor, "for")
|
|
if longMode(g, n) or
|
|
(lsub(g, n[^1]) + lsub(g, n[^2]) + 6 + g.lineLen > MaxLineLen):
|
|
incl(c.flags, rfLongMode)
|
|
gcomma(g, n, c, 0, - 3)
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkIn, "in")
|
|
gsub(g, n[^2], c)
|
|
putWithSpace(g, tkColon, ":")
|
|
gcoms(g)
|
|
gstmts(g, n[^1], c)
|
|
|
|
proc gcase(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
if n.len == 0: return
|
|
var last = if n[^1].kind == nkElse: -2 else: -1
|
|
if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
|
|
putWithSpace(g, tkCase, "case")
|
|
gcond(g, n[0])
|
|
gcoms(g)
|
|
optNL(g)
|
|
gsons(g, n, c, 1, last)
|
|
if last == - 2:
|
|
c = initContext()
|
|
if longMode(g, n[^1]): incl(c.flags, rfLongMode)
|
|
gsub(g, n[^1], c)
|
|
|
|
proc genSymSuffix(result: var string, s: PSym) {.inline.} =
|
|
if sfGenSym in s.flags and s.name.id != ord(wUnderscore):
|
|
result.add '_'
|
|
result.addInt s.id
|
|
|
|
proc gproc(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
if n[namePos].kind == nkSym:
|
|
let s = n[namePos].sym
|
|
var ret = renderDefinitionName(s)
|
|
ret.genSymSuffix(s)
|
|
put(g, tkSymbol, ret)
|
|
else:
|
|
gsub(g, n[namePos])
|
|
|
|
if n[patternPos].kind != nkEmpty:
|
|
gpattern(g, n[patternPos])
|
|
g.inside(GenericParams):
|
|
if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
|
|
n[miscPos][1].kind != nkEmpty:
|
|
gsub(g, n[miscPos][1])
|
|
else:
|
|
gsub(g, n[genericParamsPos])
|
|
gsub(g, n[paramsPos])
|
|
if renderNoPragmas notin g.flags:
|
|
gsub(g, n[pragmasPos])
|
|
if renderNoBody notin g.flags:
|
|
if n.len > bodyPos and n[bodyPos].kind != nkEmpty:
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
indentNL(g)
|
|
gcoms(g)
|
|
dedent(g)
|
|
c = initContext()
|
|
gstmts(g, n[bodyPos], c)
|
|
putNL(g)
|
|
else:
|
|
indentNL(g)
|
|
gcoms(g)
|
|
dedent(g)
|
|
|
|
proc gTypeClassTy(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
putWithSpace(g, tkConcept, "concept")
|
|
gsons(g, n[0], c) # arglist
|
|
gsub(g, n[1]) # pragmas
|
|
gsub(g, n[2]) # of
|
|
gcoms(g)
|
|
indentNL(g)
|
|
gcoms(g)
|
|
gstmts(g, n[3], c)
|
|
dedent(g)
|
|
|
|
proc gblock(g: var TSrcGen, n: PNode) =
|
|
# you shouldn't simplify it to `n.len < 2`
|
|
# because the following codes should be executed
|
|
# even when block stmt has only one child for getting
|
|
# better error messages.
|
|
if n.len == 0:
|
|
return
|
|
|
|
var c: TContext = initContext()
|
|
|
|
if n[0].kind != nkEmpty:
|
|
putWithSpace(g, tkBlock, "block")
|
|
gsub(g, n[0])
|
|
else:
|
|
put(g, tkBlock, "block")
|
|
|
|
# block stmt should have two children
|
|
if n.len == 1:
|
|
return
|
|
|
|
putWithSpace(g, tkColon, ":")
|
|
|
|
if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
|
|
incl(c.flags, rfLongMode)
|
|
gcoms(g)
|
|
gstmts(g, n[1], c)
|
|
|
|
proc gstaticStmt(g: var TSrcGen, n: PNode) =
|
|
var c: TContext = initContext()
|
|
putWithSpace(g, tkStatic, "static")
|
|
putWithSpace(g, tkColon, ":")
|
|
if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
|
|
incl(c.flags, rfLongMode)
|
|
gcoms(g) # a good place for comments
|
|
gstmts(g, n[0], c)
|
|
|
|
proc gasm(g: var TSrcGen, n: PNode) =
|
|
putWithSpace(g, tkAsm, "asm")
|
|
gsub(g, n[0])
|
|
gcoms(g)
|
|
if n.len > 1:
|
|
gsub(g, n[1])
|
|
|
|
proc gident(g: var TSrcGen, n: PNode) =
|
|
if GenericParams in g.inside and n.kind == nkSym:
|
|
if sfAnon in n.sym.flags or
|
|
(n.typ != nil and tfImplicitTypeParam in n.typ.flags): return
|
|
|
|
var t: TokType
|
|
var s = atom(g, n)
|
|
if s.len > 0 and s[0] in lexer.SymChars:
|
|
if n.kind == nkIdent:
|
|
if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
|
|
(n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
|
|
t = tkSymbol
|
|
else:
|
|
t = TokType(n.ident.id + ord(tkSymbol))
|
|
else:
|
|
t = tkSymbol
|
|
else:
|
|
t = tkOpr
|
|
if renderIr in g.flags and n.kind == nkSym:
|
|
let localId = disamb(g, n.sym)
|
|
if localId != 0 and n.sym.magic == mNone:
|
|
s.add '_'
|
|
s.addInt localId
|
|
if sfCursor in n.sym.flags:
|
|
s.add "_cursor"
|
|
elif n.kind == nkSym and (renderIds in g.flags or
|
|
(sfGenSym in n.sym.flags and n.sym.name.id != ord(wUnderscore)) or
|
|
n.sym.kind == skTemp):
|
|
s.add '_'
|
|
s.addInt n.sym.id
|
|
when defined(debugMagics):
|
|
s.add '_'
|
|
s.add $n.sym.magic
|
|
put(g, t, s, if n.kind == nkSym and renderSyms in g.flags: n.sym else: nil)
|
|
|
|
proc doParamsAux(g: var TSrcGen, params: PNode) =
|
|
if params.len > 1:
|
|
put(g, tkParLe, "(")
|
|
gsemicolon(g, params, 1)
|
|
put(g, tkParRi, ")")
|
|
|
|
if params.len > 0 and params[0].kind != nkEmpty:
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkOpr, "->")
|
|
gsub(g, params[0])
|
|
|
|
proc gsub(g: var TSrcGen; n: PNode; i: int) =
|
|
if i < n.len:
|
|
gsub(g, n[i])
|
|
else:
|
|
put(g, tkOpr, "<<" & $i & "th child missing for " & $n.kind & " >>")
|
|
|
|
type
|
|
BracketKind = enum
|
|
bkNone, bkBracket, bkBracketAsgn, bkCurly, bkCurlyAsgn
|
|
|
|
proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
|
|
if renderIds notin g.flags:
|
|
case n.kind
|
|
of nkClosedSymChoice, nkOpenSymChoice:
|
|
if n.len > 0: result = bracketKind(g, n[0])
|
|
else: result = bkNone
|
|
of nkSym:
|
|
result = case n.sym.name.s
|
|
of "[]": bkBracket
|
|
of "[]=": bkBracketAsgn
|
|
of "{}": bkCurly
|
|
of "{}=": bkCurlyAsgn
|
|
else: bkNone
|
|
else: result = bkNone
|
|
else:
|
|
result = bkNone
|
|
|
|
proc skipHiddenNodes(n: PNode): PNode =
|
|
result = n
|
|
while result != nil:
|
|
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
|
|
result = result[1]
|
|
elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
|
|
result.len > 0:
|
|
result = result[0]
|
|
else: break
|
|
|
|
proc accentedName(g: var TSrcGen, n: PNode) =
|
|
# This is for cases where ident should've really been a `nkAccQuoted`, e.g. `:tmp`
|
|
# or if user writes a macro with `ident":foo"`. It's unclear whether these should be legal.
|
|
const backticksNeeded = OpChars + {'[', '{', '\''}
|
|
if n == nil: return
|
|
let ident = n.getPIdent
|
|
if ident != nil and ident.s[0] in backticksNeeded:
|
|
put(g, tkAccent, "`")
|
|
gident(g, n)
|
|
put(g, tkAccent, "`")
|
|
else:
|
|
gsub(g, n)
|
|
|
|
proc infixArgument(g: var TSrcGen, n: PNode, i: int) =
|
|
if i < 1 or i > 2: return
|
|
var needsParenthesis = false
|
|
let nNext = n[i].skipHiddenNodes
|
|
if nNext.kind == nkInfix:
|
|
if nNext[0].kind in {nkSym, nkIdent} and n[0].kind in {nkSym, nkIdent}:
|
|
let nextId = if nNext[0].kind == nkSym: nNext[0].sym.name else: nNext[0].ident
|
|
let nnId = if n[0].kind == nkSym: n[0].sym.name else: n[0].ident
|
|
if i == 1:
|
|
if getPrecedence(nextId) < getPrecedence(nnId):
|
|
needsParenthesis = true
|
|
elif i == 2:
|
|
if getPrecedence(nextId) <= getPrecedence(nnId):
|
|
needsParenthesis = true
|
|
if needsParenthesis:
|
|
put(g, tkParLe, "(")
|
|
gsub(g, n, i)
|
|
if needsParenthesis:
|
|
put(g, tkParRi, ")")
|
|
|
|
const postExprBlocks = {nkStmtList, nkStmtListExpr,
|
|
nkOfBranch, nkElifBranch, nkElse,
|
|
nkExceptBranch, nkFinally, nkDo}
|
|
|
|
proc postStatements(g: var TSrcGen, n: PNode, i: int, fromStmtList: bool) =
|
|
var i = i
|
|
if n[i].kind in {nkStmtList, nkStmtListExpr}:
|
|
if fromStmtList:
|
|
put(g, tkColon, ":")
|
|
else:
|
|
put(g, tkSpaces, Space)
|
|
put(g, tkDo, "do")
|
|
put(g, tkColon, ":")
|
|
gsub(g, n, i)
|
|
i.inc
|
|
for j in i ..< n.len:
|
|
if n[j].kind == nkDo:
|
|
optNL(g)
|
|
elif n[j].kind in {nkStmtList, nkStmtListExpr}:
|
|
optNL(g)
|
|
put(g, tkDo, "do")
|
|
put(g, tkColon, ":")
|
|
gsub(g, n, j)
|
|
|
|
proc isCustomLit(n: PNode): bool =
|
|
if n.len == 2 and n[0].kind == nkRStrLit:
|
|
let ident = n[1].getPIdent
|
|
result = ident != nil and ident.s.startsWith('\'')
|
|
else:
|
|
result = false
|
|
|
|
proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
|
if isNil(n): return
|
|
var
|
|
a: TContext = default(TContext)
|
|
if shouldRenderComment(g, n): pushCom(g, n)
|
|
case n.kind # atoms:
|
|
of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
|
|
of nkEmpty: discard
|
|
of nkType: put(g, tkInvalid, atom(g, n))
|
|
of nkSym, nkIdent: gident(g, n)
|
|
of nkIntLit: put(g, tkIntLit, atom(g, n))
|
|
of nkInt8Lit: put(g, tkInt8Lit, atom(g, n))
|
|
of nkInt16Lit: put(g, tkInt16Lit, atom(g, n))
|
|
of nkInt32Lit: put(g, tkInt32Lit, atom(g, n))
|
|
of nkInt64Lit: put(g, tkInt64Lit, atom(g, n))
|
|
of nkUIntLit: put(g, tkUIntLit, atom(g, n))
|
|
of nkUInt8Lit: put(g, tkUInt8Lit, atom(g, n))
|
|
of nkUInt16Lit: put(g, tkUInt16Lit, atom(g, n))
|
|
of nkUInt32Lit: put(g, tkUInt32Lit, atom(g, n))
|
|
of nkUInt64Lit: put(g, tkUInt64Lit, atom(g, n))
|
|
of nkFloatLit: put(g, tkFloatLit, atom(g, n))
|
|
of nkFloat32Lit: put(g, tkFloat32Lit, atom(g, n))
|
|
of nkFloat64Lit: put(g, tkFloat64Lit, atom(g, n))
|
|
of nkFloat128Lit: put(g, tkFloat128Lit, atom(g, n))
|
|
of nkStrLit: put(g, tkStrLit, atom(g, n))
|
|
of nkRStrLit: put(g, tkRStrLit, atom(g, n))
|
|
of nkCharLit: put(g, tkCharLit, atom(g, n))
|
|
of nkNilLit: put(g, tkNil, atom(g, n)) # complex expressions
|
|
of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr:
|
|
if n.len > 1 and n.lastSon.kind in postExprBlocks:
|
|
accentedName(g, n[0])
|
|
var i = 1
|
|
while i < n.len and n[i].kind notin postExprBlocks: i.inc
|
|
if i > 1:
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n, 1, i - 1 - n.len)
|
|
put(g, tkParRi, ")")
|
|
postStatements(g, n, i, fromStmtList)
|
|
elif n.len >= 1:
|
|
case bracketKind(g, n[0])
|
|
of bkBracket:
|
|
gsub(g, n, 1)
|
|
put(g, tkBracketLe, "[")
|
|
gcomma(g, n, 2)
|
|
put(g, tkBracketRi, "]")
|
|
of bkBracketAsgn:
|
|
gsub(g, n, 1)
|
|
put(g, tkBracketLe, "[")
|
|
gcomma(g, n, 2, -2)
|
|
put(g, tkBracketRi, "]")
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, n, n.len - 1)
|
|
of bkCurly:
|
|
gsub(g, n, 1)
|
|
put(g, tkCurlyLe, "{")
|
|
gcomma(g, n, 2)
|
|
put(g, tkCurlyRi, "}")
|
|
of bkCurlyAsgn:
|
|
gsub(g, n, 1)
|
|
put(g, tkCurlyLe, "{")
|
|
gcomma(g, n, 2, -2)
|
|
put(g, tkCurlyRi, "}")
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, n, n.len - 1)
|
|
of bkNone:
|
|
accentedName(g, n[0])
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n, 1)
|
|
put(g, tkParRi, ")")
|
|
else:
|
|
put(g, tkParLe, "(")
|
|
put(g, tkParRi, ")")
|
|
of nkCallStrLit:
|
|
if n.len > 0: accentedName(g, n[0])
|
|
if n.len > 1 and n[1].kind == nkRStrLit:
|
|
put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"')
|
|
else:
|
|
gsub(g, n, 1)
|
|
of nkHiddenStdConv, nkHiddenSubConv:
|
|
if n.len >= 2:
|
|
when false:
|
|
# if {renderIds, renderIr} * g.flags != {}:
|
|
put(g, tkSymbol, "(conv)")
|
|
put(g, tkParLe, "(")
|
|
gsub(g, n[1])
|
|
put(g, tkParRi, ")")
|
|
else:
|
|
gsub(g, n[1])
|
|
else:
|
|
put(g, tkSymbol, "(wrong conv)")
|
|
of nkHiddenCallConv:
|
|
if {renderIds, renderIr} * g.flags != {}:
|
|
accentedName(g, n[0])
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n, 1)
|
|
put(g, tkParRi, ")")
|
|
elif n.len >= 2:
|
|
gsub(g, n[1])
|
|
else:
|
|
put(g, tkSymbol, "(wrong conv)")
|
|
of nkCast:
|
|
put(g, tkCast, "cast")
|
|
if n.len > 0 and n[0].kind != nkEmpty:
|
|
put(g, tkBracketLe, "[")
|
|
gsub(g, n, 0)
|
|
put(g, tkBracketRi, "]")
|
|
put(g, tkParLe, "(")
|
|
gsub(g, n, 1)
|
|
put(g, tkParRi, ")")
|
|
of nkAddr:
|
|
put(g, tkAddr, "addr")
|
|
if n.len > 0:
|
|
put(g, tkParLe, "(")
|
|
gsub(g, n[0])
|
|
put(g, tkParRi, ")")
|
|
of nkStaticExpr:
|
|
put(g, tkStatic, "static")
|
|
put(g, tkSpaces, Space)
|
|
gsub(g, n, 0)
|
|
of nkBracketExpr:
|
|
gsub(g, n, 0)
|
|
put(g, tkBracketLe, "[")
|
|
gcomma(g, n, 1)
|
|
put(g, tkBracketRi, "]")
|
|
of nkCurlyExpr:
|
|
gsub(g, n, 0)
|
|
put(g, tkCurlyLe, "{")
|
|
gcomma(g, n, 1)
|
|
put(g, tkCurlyRi, "}")
|
|
of nkPragmaExpr:
|
|
gsub(g, n, 0)
|
|
gcomma(g, n, 1)
|
|
of nkCommand:
|
|
accentedName(g, n[0])
|
|
put(g, tkSpaces, Space)
|
|
if n.len > 1 and n.lastSon.kind in postExprBlocks:
|
|
var i = 1
|
|
while i < n.len and n[i].kind notin postExprBlocks: i.inc
|
|
if i > 1:
|
|
gcomma(g, n, 1, i - 1 - n.len)
|
|
postStatements(g, n, i, fromStmtList)
|
|
else:
|
|
gcomma(g, n, 1)
|
|
of nkExprEqExpr, nkAsgn, nkFastAsgn:
|
|
gsub(g, n, 0)
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, n, 1)
|
|
of nkSinkAsgn:
|
|
put(g, tkSymbol, "`=sink`")
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n)
|
|
put(g, tkParRi, ")")
|
|
of nkChckRangeF:
|
|
put(g, tkSymbol, "chckRangeF")
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n)
|
|
put(g, tkParRi, ")")
|
|
of nkChckRange64:
|
|
put(g, tkSymbol, "chckRange64")
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n)
|
|
put(g, tkParRi, ")")
|
|
of nkChckRange:
|
|
put(g, tkSymbol, "chckRange")
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n)
|
|
put(g, tkParRi, ")")
|
|
of nkObjDownConv, nkObjUpConv:
|
|
let typ = if (n.typ != nil) and (n.typ.sym != nil): n.typ.sym.name.s else: ""
|
|
put(g, tkParLe, typ & "(")
|
|
if n.len >= 1: gsub(g, n[0])
|
|
put(g, tkParRi, ")")
|
|
of nkClosedSymChoice, nkOpenSymChoice:
|
|
if renderIds in g.flags:
|
|
put(g, tkParLe, "(")
|
|
for i in 0..<n.len:
|
|
if i > 0: put(g, tkOpr, "|")
|
|
if n[i].kind == nkSym:
|
|
let s = n[i].sym
|
|
if s.owner != nil:
|
|
put g, tkSymbol, n[i].sym.owner.name.s
|
|
put g, tkOpr, "."
|
|
put g, tkSymbol, n[i].sym.name.s
|
|
else:
|
|
gsub(g, n[i], c)
|
|
put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
|
|
else:
|
|
gsub(g, n, 0)
|
|
of nkOpenSym: gsub(g, n, 0)
|
|
of nkPar, nkClosure:
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n, c)
|
|
put(g, tkParRi, ")")
|
|
of nkTupleConstr:
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n, c)
|
|
if n.len == 1 and n[0].kind != nkExprColonExpr: put(g, tkComma, ",")
|
|
put(g, tkParRi, ")")
|
|
of nkCurly:
|
|
put(g, tkCurlyLe, "{")
|
|
gcomma(g, n, c)
|
|
put(g, tkCurlyRi, "}")
|
|
of nkArgList:
|
|
gcomma(g, n, c)
|
|
of nkTableConstr:
|
|
put(g, tkCurlyLe, "{")
|
|
if n.len > 0: gcomma(g, n, c)
|
|
else: put(g, tkColon, ":")
|
|
put(g, tkCurlyRi, "}")
|
|
of nkBracket:
|
|
put(g, tkBracketLe, "[")
|
|
gcomma(g, n, c)
|
|
put(g, tkBracketRi, "]")
|
|
of nkDotExpr:
|
|
if isCustomLit(n):
|
|
put(g, tkCustomLit, n[0].strVal)
|
|
gsub(g, n, 1)
|
|
else:
|
|
for i in 0..<n.len-1:
|
|
gsub(g, n, i)
|
|
put(g, tkDot, ".")
|
|
if n.len > 1:
|
|
accentedName(g, n[^1])
|
|
of nkBind:
|
|
putWithSpace(g, tkBind, "bind")
|
|
gsub(g, n, 0)
|
|
of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString:
|
|
if renderIds in g.flags:
|
|
put(g, tkAddr, $n.kind)
|
|
put(g, tkParLe, "(")
|
|
gsub(g, n, 0)
|
|
if renderIds in g.flags:
|
|
put(g, tkParRi, ")")
|
|
|
|
of nkLambda:
|
|
putWithSpace(g, tkProc, "proc")
|
|
gsub(g, n, paramsPos)
|
|
gsub(g, n, pragmasPos)
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, n, bodyPos)
|
|
of nkDo:
|
|
putWithSpace(g, tkDo, "do")
|
|
if paramsPos < n.len:
|
|
doParamsAux(g, n[paramsPos])
|
|
gsub(g, n, pragmasPos)
|
|
put(g, tkColon, ":")
|
|
gsub(g, n, bodyPos)
|
|
of nkIdentDefs:
|
|
var exclFlags: TRenderFlags = {}
|
|
if ObjectDef in g.inside:
|
|
if not n[0].isExported() and renderNonExportedFields notin g.flags:
|
|
# Skip if this is a property in a type and its not exported
|
|
# (While also not allowing rendering of non exported fields)
|
|
return
|
|
# render postfix for object fields:
|
|
exclFlags = g.flags * {renderNoPostfix}
|
|
# We render the identDef without being inside the section incase we render something like
|
|
# y: proc (x: string) # (We wouldn't want to check if x is exported)
|
|
g.outside(ObjectDef):
|
|
g.flags.excl(exclFlags)
|
|
gcomma(g, n, 0, -3)
|
|
g.flags.incl(exclFlags)
|
|
if n.len >= 2 and n[^2].kind != nkEmpty:
|
|
putWithSpace(g, tkColon, ":")
|
|
gsub(g, n[^2], c)
|
|
elif n.referencesUsing and renderExpandUsing in g.flags:
|
|
putWithSpace(g, tkColon, ":")
|
|
gsub(g, newSymNode(n.origUsingType), c)
|
|
|
|
if n.len >= 1 and n[^1].kind != nkEmpty:
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, n[^1], c)
|
|
of nkConstDef:
|
|
gcomma(g, n, 0, -3)
|
|
if n.len >= 2 and n[^2].kind != nkEmpty:
|
|
putWithSpace(g, tkColon, ":")
|
|
gsub(g, n[^2], c)
|
|
|
|
if n.len >= 1 and n[^1].kind != nkEmpty:
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, n[^1], c)
|
|
of nkVarTuple:
|
|
if n[^1].kind == nkEmpty:
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n, 0, -2)
|
|
put(g, tkParRi, ")")
|
|
else:
|
|
put(g, tkParLe, "(")
|
|
gcomma(g, n, 0, -3)
|
|
put(g, tkParRi, ")")
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, lastSon(n), c)
|
|
of nkExprColonExpr:
|
|
gsub(g, n, 0)
|
|
putWithSpace(g, tkColon, ":")
|
|
gsub(g, n, 1)
|
|
of nkInfix:
|
|
if n.len < 3:
|
|
var i = 0
|
|
put(g, tkOpr, "Too few children for nkInfix")
|
|
return
|
|
let oldLineLen = g.lineLen # we cache this because lineLen gets updated below
|
|
infixArgument(g, n, 1)
|
|
put(g, tkSpaces, Space)
|
|
gsub(g, n, 0) # binary operator
|
|
# e.g.: `n1 == n2` decompses as following sum:
|
|
if n.len == 3 and not fits(g, oldLineLen + lsub(g, n[1]) + lsub(g, n[2]) + lsub(g, n[0]) + len(" ")):
|
|
optNL(g, g.indent + longIndentWid)
|
|
else:
|
|
put(g, tkSpaces, Space)
|
|
infixArgument(g, n, 2)
|
|
if n.len > 3 and n.lastSon.kind in postExprBlocks:
|
|
var i = 3
|
|
while i < n.len and n[i].kind notin postExprBlocks: i.inc
|
|
postStatements(g, n, i, fromStmtList)
|
|
of nkPrefix:
|
|
gsub(g, n, 0)
|
|
if n.len > 1:
|
|
let opr = if n[0].kind == nkIdent: n[0].ident
|
|
elif n[0].kind == nkSym: n[0].sym.name
|
|
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
|
|
else: nil
|
|
let nNext = skipHiddenNodes(n[1])
|
|
if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
|
put(g, tkSpaces, Space)
|
|
if nNext.kind == nkInfix:
|
|
put(g, tkParLe, "(")
|
|
gsub(g, n[1])
|
|
put(g, tkParRi, ")")
|
|
else:
|
|
gsub(g, n[1])
|
|
if n.len > 2 and n.lastSon.kind in postExprBlocks:
|
|
var i = 2
|
|
while i < n.len and n[i].kind notin postExprBlocks: i.inc
|
|
postStatements(g, n, i, fromStmtList)
|
|
of nkPostfix:
|
|
gsub(g, n, 1)
|
|
if renderNoPostfix notin g.flags:
|
|
gsub(g, n, 0)
|
|
of nkRange:
|
|
gsub(g, n, 0)
|
|
put(g, tkDotDot, "..")
|
|
gsub(g, n, 1)
|
|
of nkDerefExpr:
|
|
gsub(g, n, 0)
|
|
put(g, tkOpr, "[]")
|
|
of nkAccQuoted:
|
|
put(g, tkAccent, "`")
|
|
for i in 0..<n.len:
|
|
proc isAlpha(n: PNode): bool =
|
|
if n.kind in {nkIdent, nkSym}:
|
|
let tmp = n.getPIdent.s
|
|
result = tmp.len > 0 and tmp[0] in {'a'..'z', 'A'..'Z'}
|
|
else:
|
|
result = false
|
|
var useSpace = false
|
|
if i == 1 and n[0].kind == nkIdent and n[0].ident.s in ["=", "'"]:
|
|
if not n[1].isAlpha: # handle `=destroy`, `'big'
|
|
useSpace = true
|
|
elif i == 1 and n[1].kind == nkIdent and n[1].ident.s == "=":
|
|
if not n[0].isAlpha: # handle setters, e.g. `foo=`
|
|
useSpace = true
|
|
elif i > 0: useSpace = true
|
|
if useSpace: put(g, tkSpaces, Space)
|
|
gsub(g, n[i])
|
|
put(g, tkAccent, "`")
|
|
of nkIfExpr:
|
|
putWithSpace(g, tkIf, "if")
|
|
if n.len > 0: gcond(g, n[0][0])
|
|
putWithSpace(g, tkColon, ":")
|
|
if n.len > 0: gsub(g, n[0], 1)
|
|
gsons(g, n, emptyContext, 1)
|
|
of nkElifExpr:
|
|
putWithSpace(g, tkElif, " elif")
|
|
gcond(g, n[0])
|
|
putWithSpace(g, tkColon, ":")
|
|
gsub(g, n, 1)
|
|
of nkElseExpr:
|
|
put(g, tkElse, " else")
|
|
putWithSpace(g, tkColon, ":")
|
|
gsub(g, n, 0)
|
|
of nkTypeOfExpr:
|
|
put(g, tkType, "typeof")
|
|
put(g, tkParLe, "(")
|
|
if n.len > 0: gsub(g, n[0])
|
|
put(g, tkParRi, ")")
|
|
of nkRefTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkRef, "ref")
|
|
gsub(g, n[0])
|
|
else:
|
|
put(g, tkRef, "ref")
|
|
of nkPtrTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkPtr, "ptr")
|
|
gsub(g, n[0])
|
|
else:
|
|
put(g, tkPtr, "ptr")
|
|
of nkVarTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkVar, "var")
|
|
gsub(g, n[0])
|
|
else:
|
|
put(g, tkVar, "var")
|
|
of nkOutTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkOut, "out")
|
|
gsub(g, n[0])
|
|
else:
|
|
put(g, tkOut, "out")
|
|
of nkDistinctTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkDistinct, "distinct")
|
|
gsub(g, n[0])
|
|
if n.len > 1:
|
|
if n[1].kind == nkWith:
|
|
putWithSpace(g, tkSymbol, " with")
|
|
else:
|
|
putWithSpace(g, tkSymbol, " without")
|
|
gcomma(g, n[1])
|
|
else:
|
|
put(g, tkDistinct, "distinct")
|
|
of nkTypeDef:
|
|
if n[0].kind == nkPragmaExpr:
|
|
# generate pragma after generic
|
|
gsub(g, n[0], 0)
|
|
gsub(g, n, 1)
|
|
gsub(g, n[0], 1)
|
|
else:
|
|
gsub(g, n, 0)
|
|
gsub(g, n, 1)
|
|
put(g, tkSpaces, Space)
|
|
if n.len > 2 and n[2].kind != nkEmpty:
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, n[2])
|
|
of nkObjectTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkObject, "object")
|
|
g.inside(ObjectDef):
|
|
gsub(g, n[0])
|
|
gsub(g, n[1])
|
|
gcoms(g)
|
|
indentNL(g)
|
|
gsub(g, n[2])
|
|
dedent(g)
|
|
else:
|
|
put(g, tkObject, "object")
|
|
of nkRecList:
|
|
for i in 0..<n.len:
|
|
optNL(g)
|
|
gsub(g, n[i], c)
|
|
gcoms(g)
|
|
of nkOfInherit:
|
|
putWithSpace(g, tkOf, "of")
|
|
gsub(g, n, 0)
|
|
of nkProcTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkProc, "proc")
|
|
gsub(g, n, 0)
|
|
gsub(g, n, 1)
|
|
else:
|
|
put(g, tkProc, "proc")
|
|
of nkIteratorTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkIterator, "iterator")
|
|
gsub(g, n, 0)
|
|
gsub(g, n, 1)
|
|
else:
|
|
put(g, tkIterator, "iterator")
|
|
of nkStaticTy:
|
|
put(g, tkStatic, "static")
|
|
put(g, tkBracketLe, "[")
|
|
if n.len > 0:
|
|
gsub(g, n[0])
|
|
put(g, tkBracketRi, "]")
|
|
of nkEnumTy:
|
|
if n.len > 0:
|
|
putWithSpace(g, tkEnum, "enum")
|
|
gsub(g, n[0])
|
|
gcoms(g)
|
|
indentNL(g)
|
|
gcommaAux(g, n, g.indent, 1)
|
|
gcoms(g) # BUGFIX: comment for the last enum field
|
|
dedent(g)
|
|
else:
|
|
put(g, tkEnum, "enum")
|
|
of nkEnumFieldDef:
|
|
gsub(g, n, 0)
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkEquals, "=")
|
|
gsub(g, n, 1)
|
|
of nkStmtList, nkStmtListExpr, nkStmtListType:
|
|
if n.len == 1 and n[0].kind == nkDiscardStmt:
|
|
put(g, tkParLe, "(")
|
|
gsub(g, n[0])
|
|
put(g, tkParRi, ")")
|
|
else:
|
|
gstmts(g, n, emptyContext)
|
|
of nkIfStmt:
|
|
putWithSpace(g, tkIf, "if")
|
|
gif(g, n)
|
|
of nkWhen, nkRecWhen:
|
|
putWithSpace(g, tkWhen, "when")
|
|
gif(g, n)
|
|
of nkWhileStmt: gwhile(g, n)
|
|
of nkPragmaBlock: gpragmaBlock(g, n)
|
|
of nkCaseStmt, nkRecCase: gcase(g, n)
|
|
of nkTryStmt, nkHiddenTryStmt: gtry(g, n)
|
|
of nkForStmt, nkParForStmt: gfor(g, n)
|
|
of nkBlockStmt, nkBlockExpr: gblock(g, n)
|
|
of nkStaticStmt: gstaticStmt(g, n)
|
|
of nkAsmStmt: gasm(g, n)
|
|
of nkProcDef:
|
|
if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc")
|
|
gproc(g, n)
|
|
of nkFuncDef:
|
|
if renderNoProcDefs notin g.flags: putWithSpace(g, tkFunc, "func")
|
|
gproc(g, n)
|
|
of nkConverterDef:
|
|
if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter")
|
|
gproc(g, n)
|
|
of nkMethodDef:
|
|
if renderNoProcDefs notin g.flags: putWithSpace(g, tkMethod, "method")
|
|
gproc(g, n)
|
|
of nkIteratorDef:
|
|
if renderNoProcDefs notin g.flags: putWithSpace(g, tkIterator, "iterator")
|
|
gproc(g, n)
|
|
of nkMacroDef:
|
|
if renderNoProcDefs notin g.flags: putWithSpace(g, tkMacro, "macro")
|
|
gproc(g, n)
|
|
of nkTemplateDef:
|
|
if renderNoProcDefs notin g.flags: putWithSpace(g, tkTemplate, "template")
|
|
gproc(g, n)
|
|
of nkTypeSection:
|
|
gsection(g, n, emptyContext, tkType, "type")
|
|
of nkConstSection:
|
|
a = initContext()
|
|
incl(a.flags, rfInConstExpr)
|
|
gsection(g, n, a, tkConst, "const")
|
|
of nkVarSection, nkLetSection, nkUsingStmt:
|
|
if n.len == 0: return
|
|
if n.kind == nkVarSection: putWithSpace(g, tkVar, "var")
|
|
elif n.kind == nkLetSection: putWithSpace(g, tkLet, "let")
|
|
else: putWithSpace(g, tkUsing, "using")
|
|
if n.len > 1:
|
|
gcoms(g)
|
|
indentNL(g)
|
|
for i in 0..<n.len:
|
|
optNL(g)
|
|
gsub(g, n[i])
|
|
gcoms(g)
|
|
dedent(g)
|
|
else:
|
|
gsub(g, n[0])
|
|
of nkReturnStmt:
|
|
putWithSpace(g, tkReturn, "return")
|
|
if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags:
|
|
gsub(g, n[0], 1)
|
|
else:
|
|
gsub(g, n, 0)
|
|
of nkRaiseStmt:
|
|
putWithSpace(g, tkRaise, "raise")
|
|
gsub(g, n, 0)
|
|
of nkYieldStmt:
|
|
putWithSpace(g, tkYield, "yield")
|
|
gsub(g, n, 0)
|
|
of nkDiscardStmt:
|
|
putWithSpace(g, tkDiscard, "discard")
|
|
gsub(g, n, 0)
|
|
of nkBreakStmt:
|
|
putWithSpace(g, tkBreak, "break")
|
|
gsub(g, n, 0)
|
|
of nkContinueStmt:
|
|
putWithSpace(g, tkContinue, "continue")
|
|
gsub(g, n, 0)
|
|
of nkPragma:
|
|
if g.inPragma <= 0:
|
|
inc g.inPragma
|
|
#if not previousNL(g):
|
|
put(g, tkSpaces, Space)
|
|
put(g, tkCurlyDotLe, "{.")
|
|
gcomma(g, n, emptyContext)
|
|
put(g, tkCurlyDotRi, ".}")
|
|
dec g.inPragma
|
|
else:
|
|
gcomma(g, n, emptyContext)
|
|
of nkImportStmt, nkExportStmt:
|
|
if n.kind == nkImportStmt:
|
|
putWithSpace(g, tkImport, "import")
|
|
else:
|
|
putWithSpace(g, tkExport, "export")
|
|
gcoms(g)
|
|
indentNL(g)
|
|
gcommaAux(g, n, g.indent)
|
|
dedent(g)
|
|
putNL(g)
|
|
of nkImportExceptStmt, nkExportExceptStmt:
|
|
if n.kind == nkImportExceptStmt:
|
|
putWithSpace(g, tkImport, "import")
|
|
else:
|
|
putWithSpace(g, tkExport, "export")
|
|
gsub(g, n, 0)
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkExcept, "except")
|
|
gcommaAux(g, n, g.indent, 1)
|
|
gcoms(g)
|
|
putNL(g)
|
|
of nkFromStmt:
|
|
putWithSpace(g, tkFrom, "from")
|
|
gsub(g, n, 0)
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkImport, "import")
|
|
gcomma(g, n, emptyContext, 1)
|
|
putNL(g)
|
|
of nkIncludeStmt:
|
|
putWithSpace(g, tkInclude, "include")
|
|
gcoms(g)
|
|
indentNL(g)
|
|
gcommaAux(g, n, g.indent)
|
|
dedent(g)
|
|
putNL(g)
|
|
of nkCommentStmt:
|
|
gcoms(g)
|
|
optNL(g)
|
|
of nkOfBranch:
|
|
optNL(g)
|
|
putWithSpace(g, tkOf, "of")
|
|
gcomma(g, n, c, 0, - 2)
|
|
putWithSpace(g, tkColon, ":")
|
|
gcoms(g)
|
|
gstmts(g, lastSon(n), c)
|
|
of nkImportAs:
|
|
gsub(g, n, 0)
|
|
put(g, tkSpaces, Space)
|
|
putWithSpace(g, tkAs, "as")
|
|
gsub(g, n, 1)
|
|
of nkBindStmt:
|
|
putWithSpace(g, tkBind, "bind")
|
|
gcomma(g, n, c)
|
|
of nkMixinStmt:
|
|
putWithSpace(g, tkMixin, "mixin")
|
|
gcomma(g, n, c)
|
|
of nkElifBranch:
|
|
optNL(g)
|
|
putWithSpace(g, tkElif, "elif")
|
|
gsub(g, n, 0)
|
|
putWithSpace(g, tkColon, ":")
|
|
gcoms(g)
|
|
gstmts(g, n[1], c)
|
|
of nkElse:
|
|
optNL(g)
|
|
put(g, tkElse, "else")
|
|
putWithSpace(g, tkColon, ":")
|
|
gcoms(g)
|
|
gstmts(g, n[0], c)
|
|
of nkFinally, nkDefer:
|
|
optNL(g)
|
|
if n.kind == nkFinally:
|
|
put(g, tkFinally, "finally")
|
|
else:
|
|
put(g, tkDefer, "defer")
|
|
putWithSpace(g, tkColon, ":")
|
|
gcoms(g)
|
|
gstmts(g, n[0], c)
|
|
of nkExceptBranch:
|
|
optNL(g)
|
|
if n.len != 1:
|
|
putWithSpace(g, tkExcept, "except")
|
|
else:
|
|
put(g, tkExcept, "except")
|
|
gcomma(g, n, 0, -2)
|
|
putWithSpace(g, tkColon, ":")
|
|
gcoms(g)
|
|
gstmts(g, lastSon(n), c)
|
|
of nkGenericParams:
|
|
proc hasExplicitParams(gp: PNode): bool =
|
|
for p in gp:
|
|
if p.typ == nil or tfImplicitTypeParam notin p.typ.flags:
|
|
return true
|
|
return false
|
|
|
|
if n.hasExplicitParams:
|
|
put(g, tkBracketLe, "[")
|
|
gsemicolon(g, n)
|
|
put(g, tkBracketRi, "]")
|
|
of nkFormalParams:
|
|
put(g, tkParLe, "(")
|
|
gsemicolon(g, n, 1)
|
|
put(g, tkParRi, ")")
|
|
if n.len > 0 and n[0].kind != nkEmpty:
|
|
putWithSpace(g, tkColon, ":")
|
|
gsub(g, n[0])
|
|
of nkTupleTy:
|
|
put(g, tkTuple, "tuple")
|
|
put(g, tkBracketLe, "[")
|
|
gcomma(g, n)
|
|
put(g, tkBracketRi, "]")
|
|
of nkTupleClassTy:
|
|
put(g, tkTuple, "tuple")
|
|
of nkComesFrom:
|
|
put(g, tkParLe, "(ComesFrom|")
|
|
gsub(g, n, 0)
|
|
put(g, tkParRi, ")")
|
|
of nkGotoState:
|
|
var c: TContext = initContext()
|
|
putWithSpace g, tkSymbol, "goto"
|
|
gsons(g, n, c)
|
|
of nkState:
|
|
var c: TContext = initContext()
|
|
putWithSpace g, tkSymbol, "state"
|
|
gsub(g, n[0], c)
|
|
putWithSpace(g, tkColon, ":")
|
|
indentNL(g)
|
|
gsons(g, n, c, 1)
|
|
dedent(g)
|
|
|
|
of nkBreakState:
|
|
put(g, tkTuple, "breakstate")
|
|
if renderIds in g.flags:
|
|
gsons(g, n, c, 0)
|
|
of nkTypeClassTy:
|
|
gTypeClassTy(g, n)
|
|
of nkError:
|
|
putWithSpace(g, tkSymbol, "error")
|
|
#gcomma(g, n, c)
|
|
gsub(g, n[0], c)
|
|
else:
|
|
#nkNone, nkExplicitTypeListCall:
|
|
internalError(g.config, n.info, "renderer.gsub(" & $n.kind & ')')
|
|
|
|
proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
|
|
if n == nil: return "<nil tree>"
|
|
var g: TSrcGen = initSrcGen(renderFlags, newPartialConfigRef())
|
|
# do not indent the initial statement list so that
|
|
# writeFile("file.nim", repr n)
|
|
# produces working Nim code:
|
|
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
|
|
gstmts(g, n, emptyContext, doIndent = false)
|
|
else:
|
|
gsub(g, n)
|
|
result = g.buf
|
|
|
|
proc `$`*(n: PNode): string = n.renderTree
|
|
|
|
proc renderModule*(n: PNode, outfile: string,
|
|
renderFlags: TRenderFlags = {};
|
|
fid = FileIndex(-1);
|
|
conf: ConfigRef = nil) =
|
|
var
|
|
f: File = default(File)
|
|
g: TSrcGen = initSrcGen(renderFlags, conf)
|
|
g.fid = fid
|
|
for i in 0..<n.len:
|
|
gsub(g, n[i])
|
|
optNL(g)
|
|
case n[i].kind
|
|
of nkTypeSection, nkConstSection, nkVarSection, nkLetSection,
|
|
nkCommentStmt: putNL(g)
|
|
else: discard
|
|
gcoms(g)
|
|
if open(f, outfile, fmWrite):
|
|
write(f, g.buf)
|
|
close(f)
|
|
else:
|
|
rawMessage(g.config, errGenerated, "cannot open file: " & outfile)
|
|
|
|
proc initTokRender*(n: PNode, renderFlags: TRenderFlags = {}): TSrcGen =
|
|
result = initSrcGen(renderFlags, newPartialConfigRef())
|
|
gsub(result, n)
|
|
|
|
proc getNextTok*(r: var TSrcGen, kind: var TokType, literal: var string) =
|
|
if r.idx < r.tokens.len:
|
|
kind = r.tokens[r.idx].kind
|
|
let length = r.tokens[r.idx].length.int
|
|
literal = substr(r.buf, r.pos, r.pos + length - 1)
|
|
inc(r.pos, length)
|
|
inc(r.idx)
|
|
else:
|
|
kind = tkEof
|
|
|
|
proc getTokSym*(r: TSrcGen): PSym =
|
|
if r.idx > 0 and r.idx <= r.tokens.len:
|
|
result = r.tokens[r.idx-1].sym
|
|
else:
|
|
result = nil
|