diff options
Diffstat (limited to 'compiler/renderer.nim')
-rw-r--r-- | compiler/renderer.nim | 655 |
1 files changed, 446 insertions, 209 deletions
diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 4b8c78cc7..cc07c0c2d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -9,28 +9,39 @@ # This module implements the renderer of the standard Nim representation. -when defined(nimHasUsed): - # 'import renderer' is so useful for debugging - # that Nim shouldn't produce a warning for that: - {.used.} +# 'import renderer' is so useful for debugging +# that Nim shouldn't produce a warning for that: +{.used.} import - lexer, options, idents, strutils, ast, msgs, lineinfos + 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 + renderNoPragmas, renderIds, renderNoProcDefs, renderSyms, renderRunnableExamples, + renderIr, renderNonExportedFields, renderExpandUsing, renderNoPostfix + TRenderFlags* = set[TRenderFlag] TRenderTok* = object - kind*: TTokType + 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 @@ -40,22 +51,45 @@ type pendingWhitespace: int comStack*: seq[PNode] # comment stack flags*: TRenderFlags - inGenericParams: bool + 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. @@ -69,9 +103,30 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string = 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) = @@ -90,30 +145,19 @@ when defined(nimpretty): proc lineDiff(a, b: PNode): int = result = minmaxLine(b)[0] - minmaxLine(a)[1] -const - MaxLineLen = 80 - LineCommentColumn = 30 +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 initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) = - g.comStack = @[] - g.tokens = @[] - g.indent = 0 - g.lineLen = 0 - g.pos = 0 - g.idx = 0 - g.buf = "" - g.flags = renderFlags - g.pendingNL = -1 - g.pendingWhitespace = -1 - g.inGenericParams = false - g.config = config - -proc addTok(g: var TSrcGen, kind: TTokType, s: string; sym: PSym = nil) = - setLen(g.tokens, g.tokens.len + 1) - g.tokens[^1].kind = kind - g.tokens[^1].length = int16(s.len) - g.tokens[^1].sym = sym +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: @@ -123,6 +167,7 @@ proc addPendingNL(g: var TSrcGen) = 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: @@ -131,7 +176,10 @@ proc addPendingNL(g: var TSrcGen) = proc putNL(g: var TSrcGen, indent: int) = if g.pendingNL >= 0: addPendingNL(g) - else: addTok(g, tkSpaces, "\n") + else: + addTok(g, tkSpaces, "\n") + g.col = 0 + g.pendingNL = indent g.lineLen = indent g.pendingWhitespace = -1 @@ -146,6 +194,7 @@ proc putNL(g: var TSrcGen) = 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) = @@ -154,6 +203,7 @@ proc optNL(g: var TSrcGen) = 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) = @@ -168,38 +218,39 @@ proc dedent(g: var TSrcGen) = dec(g.pendingNL, IndentWidth) dec(g.lineLen, IndentWidth) -proc put(g: var TSrcGen, kind: TTokType, s: string; sym: PSym = nil) = +proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = if kind != tkSpaces: addPendingNL(g) - if s.len > 0: + if s.len > 0 or kind in {tkHideableStart, tkHideableEnd}: addTok(g, kind, s, sym) - inc(g.lineLen, s.len) 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 - var isCode = (s.len >= 2) and (s[1] != ' ') - var ind = g.lineLen + let isCode = (s.len >= 2) and (s[1] != ' ') + let ind = g.col var com = "## " while i <= hi: case s[i] of '\0': break - of '\x0D': + of '\r': put(g, tkComment, com) com = "## " inc(i) - if i <= hi and s[i] == '\x0A': inc(i) + if i <= hi and s[i] == '\n': inc(i) optNL(g, ind) - of '\x0A': + of '\n': put(g, tkComment, com) com = "## " inc(i) optNL(g, ind) - of ' ', '\x09': + of ' ', '\t': com.add(s[i]) inc(i) else: @@ -208,7 +259,7 @@ proc putComment(g: var TSrcGen, s: string) = # compute length of the following word: var j = i while j <= hi and s[j] > ' ': inc(j) - if not isCode and (g.lineLen + (j - i) > MaxLineLen): + if not isCode and (g.col + (j - i) > MaxLineLen): put(g, tkComment, com) optNL(g, ind) com = "## " @@ -219,6 +270,7 @@ proc putComment(g: var TSrcGen, s: string) = optNL(g) proc maxLineLength(s: string): int = + result = 0 if s.len == 0: return 0 var i = 0 let hi = s.len - 1 @@ -227,12 +279,12 @@ proc maxLineLength(s: string): int = case s[i] of '\0': break - of '\x0D': + of '\r': inc(i) - if i <= hi and s[i] == '\x0A': inc(i) + if i <= hi and s[i] == '\n': inc(i) result = max(result, lineLen) lineLen = 0 - of '\x0A': + of '\n': inc(i) result = max(result, lineLen) lineLen = 0 @@ -240,19 +292,19 @@ proc maxLineLength(s: string): int = inc(lineLen) inc(i) -proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = +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 '\x0D': + of '\r': put(g, kind, str) str = "" inc(i) - if i <= hi and s[i] == '\x0A': inc(i) + if i <= hi and s[i] == '\n': inc(i) optNL(g, 0) - of '\x0A': + of '\n': put(g, kind, str) str = "" inc(i) @@ -265,7 +317,7 @@ proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = proc containsNL(s: string): bool = for i in 0..<s.len: case s[i] - of '\x0D', '\x0A': + of '\r', '\n': return true else: discard @@ -281,24 +333,27 @@ proc popAllComs(g: var TSrcGen) = const Space = " " -proc shouldRenderComment(g: var TSrcGen, n: PNode): bool = - result = false - if n.comment.len > 0: - result = (renderNoComments notin g.flags) or - (renderDocComments in g.flags) +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.lineLen < LineCommentColumn): + (g.col < LineCommentColumn): var ml = maxLineLength(n.comment) if ml + LineCommentColumn <= MaxLineLen: - put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen)) + 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) = @@ -311,8 +366,9 @@ proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = result = t while result != nil and result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}: - result = lastSon(result) + result = skipModifier(result) + result = "" let typ = n.typ.skip if typ != nil and typ.kind in {tyBool, tyEnum}: if sfPure in typ.sym.flags: @@ -326,7 +382,7 @@ proc litAux(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: - var y = if size < sizeof(BiggestInt): x and ((1 shl (size*8)) - 1) + 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) @@ -354,6 +410,7 @@ proc atom(g: TSrcGen; n: PNode): string = 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 & "\"\"\"" @@ -373,24 +430,29 @@ proc atom(g: TSrcGen; n: PNode): string = 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[PInt64](addr(n.floatVal)))[] , 8) + 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[PInt32](addr(f)))[], 4) & "\'f32" + 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[PInt64](addr(n.floatVal)))[], 8) & "\'f64" + 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, "rnimsyn.atom " & $n.kind) + internalError(g.config, "renderer.atom " & $n.kind) result = "" proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = @@ -409,10 +471,29 @@ proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = 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 n.comment.len > 0: return MaxLineLen + 1 + if shouldRenderComment(g, n): return MaxLineLen + 1 case n.kind of nkEmpty: result = 0 of nkTripleStrLit: @@ -438,6 +519,7 @@ proc lsub(g: TSrcGen; n: PNode): int = 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 @@ -449,8 +531,11 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkDo: result = lsons(g, n) + len("do__:_") of nkConstDef, nkIdentDefs: result = lcomma(g, n, 0, - 3) - if n[^2].kind != nkEmpty: result = result + lsub(g, n[^2]) + 2 - if n[^1].kind != nkEmpty: result = result + lsub(g, n[^1]) + 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("()") @@ -461,13 +546,17 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n) of nkObjDownConv, nkObjUpConv: result = 2 - if n.len >= 1: result = result + lsub(g, n[0]) - result = result + lcomma(g, n, 1) + 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: result = lsons(g, n) + 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 @@ -481,7 +570,7 @@ proc lsub(g: TSrcGen; n: PNode): int = 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: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var") + 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: @@ -493,7 +582,7 @@ proc lsub(g: TSrcGen; n: PNode): int = 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 nkSharedTy: result = lsons(g, n) + len("shared_") + 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_") @@ -507,7 +596,7 @@ proc lsub(g: TSrcGen; n: PNode): int = 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: + 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]) @@ -526,7 +615,7 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkGenericParams: result = lcomma(g, n) + 2 of nkFormalParams: result = lcomma(g, n, 1) + 2 - if n[0].kind != nkEmpty: result = result + lsub(g, n[0]) + 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: @@ -545,15 +634,13 @@ type const emptyContext: TContext = (spacing: 0, flags: {}) -proc initContext(c: var TContext) = - c.spacing = 0 - c.flags = {} +proc initContext(): TContext = + result = (spacing: 0, flags: {}) -proc gsub(g: var TSrcGen, n: PNode, c: TContext) -proc gsub(g: var TSrcGen, n: PNode) = - var c: TContext - initContext(c) - gsub(g, n, c) +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 @@ -565,27 +652,49 @@ proc hasCom(n: PNode): bool = for i in 0..<n.len: if hasCom(n[i]): return true -proc putWithSpace(g: var TSrcGen, kind: TTokType, s: string) = +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: - var c = i < n.len + theEnd - var sublen = lsub(g, n[i]) + ord(c) + 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, TokTypeToStr[separator]) - if hasCom(n[i]): + 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) = + theEnd: int = -1) = var ind: int if rfInConstExpr in c.flags: ind = g.indent + IndentWidth @@ -608,7 +717,7 @@ 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: TTokType, +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) @@ -621,7 +730,7 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType, dedent(g) proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool = - result = n.comment.len > 0 + result = shouldRenderComment(g, n) if not result: # check further for i in start..n.len + theEnd: @@ -641,7 +750,7 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) = if n[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: gstmts(g, n[i], c, doIndent=false) else: - gsub(g, n[i]) + gsub(g, n[i], fromStmtList = true) gcoms(g) if doIndent: dedent(g) else: @@ -660,9 +769,8 @@ proc gcond(g: var TSrcGen, n: PNode) = put(g, tkParRi, ")") proc gif(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() gcond(g, n[0][0]) - initContext(c) putWithSpace(g, tkColon, ":") if longMode(g, n) or (lsub(g, n[0][1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) @@ -673,20 +781,18 @@ proc gif(g: var TSrcGen, n: PNode) = gsub(g, n[i], c) proc gwhile(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() putWithSpace(g, tkWhile, "while") gcond(g, n[0]) putWithSpace(g, tkColon, ":") - initContext(c) 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 + var c: TContext = initContext() put(g, tkCurlyLe, "{") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -694,20 +800,18 @@ proc gpattern(g: var TSrcGen, n: PNode) = put(g, tkCurlyRi, "}") proc gpragmaBlock(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() gsub(g, n[0]) putWithSpace(g, tkColon, ":") - initContext(c) 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 + var c: TContext = initContext() put(g, tkTry, "try") putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -715,9 +819,8 @@ proc gtry(g: var TSrcGen, n: PNode) = gsons(g, n, c, 1) proc gfor(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() putWithSpace(g, tkFor, "for") - initContext(c) if longMode(g, n) or (lsub(g, n[^1]) + lsub(g, n[^2]) + 6 + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) @@ -730,8 +833,7 @@ proc gfor(g: var TSrcGen, n: PNode) = gstmts(g, n[^1], c) proc gcase(g: var TSrcGen, n: PNode) = - var c: TContext - initContext(c) + 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) @@ -741,17 +843,17 @@ proc gcase(g: var TSrcGen, n: PNode) = optNL(g) gsons(g, n, c, 1, last) if last == - 2: - initContext(c) + 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: + 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 + var c: TContext = initContext() if n[namePos].kind == nkSym: let s = n[namePos].sym var ret = renderDefinitionName(s) @@ -762,25 +864,23 @@ proc gproc(g: var TSrcGen, n: PNode) = if n[patternPos].kind != nkEmpty: gpattern(g, n[patternPos]) - let oldInGenericParams = g.inGenericParams - g.inGenericParams = true - 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]) - g.inGenericParams = oldInGenericParams + 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[bodyPos].kind != nkEmpty: + if n.len > bodyPos and n[bodyPos].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") indentNL(g) gcoms(g) dedent(g) - initContext(c) + c = initContext() gstmts(g, n[bodyPos], c) putNL(g) else: @@ -789,8 +889,7 @@ proc gproc(g: var TSrcGen, n: PNode) = dedent(g) proc gTypeClassTy(g: var TSrcGen, n: PNode) = - var c: TContext - initContext(c) + var c: TContext = initContext() putWithSpace(g, tkConcept, "concept") gsons(g, n[0], c) # arglist gsub(g, n[1]) # pragmas @@ -802,24 +901,36 @@ proc gTypeClassTy(g: var TSrcGen, n: PNode) = dedent(g) proc gblock(g: var TSrcGen, n: PNode) = - var c: TContext - initContext(c) + # 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 + var c: TContext = initContext() putWithSpace(g, tkStatic, "static") putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -833,11 +944,11 @@ proc gasm(g: var TSrcGen, n: PNode) = gsub(g, n[1]) proc gident(g: var TSrcGen, n: PNode) = - if g.inGenericParams and n.kind == nkSym: + 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: TTokType + var t: TokType var s = atom(g, n) if s.len > 0 and s[0] in lexer.SymChars: if n.kind == nkIdent: @@ -845,12 +956,21 @@ proc gident(g: var TSrcGen, n: PNode) = (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): t = tkSymbol else: - t = TTokType(n.ident.id + ord(tkSymbol)) + t = TokType(n.ident.id + ord(tkSymbol)) else: t = tkSymbol else: t = tkOpr - if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp): + 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): @@ -865,6 +985,7 @@ proc doParamsAux(g: var TSrcGen, params: PNode) = put(g, tkParRi, ")") if params.len > 0 and params[0].kind != nkEmpty: + put(g, tkSpaces, Space) putWithSpace(g, tkOpr, "->") gsub(g, params[0]) @@ -883,6 +1004,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = 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 @@ -891,11 +1013,13 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = 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} and result.len > 1: + 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: @@ -903,14 +1027,12 @@ proc skipHiddenNodes(n: PNode): PNode = else: break proc accentedName(g: var TSrcGen, n: PNode) = - const backticksNeeded = OpChars + {'[', '{'} + # 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 isOperator = - if n.kind == nkIdent and n.ident.s.len > 0 and n.ident.s[0] in backticksNeeded: true - elif n.kind == nkSym and n.sym.name.s.len > 0 and n.sym.name.s[0] in backticksNeeded: true - else: false - - if isOperator: + let ident = n.getPIdent + if ident != nil and ident.s[0] in backticksNeeded: put(g, tkAccent, "`") gident(g, n) put(g, tkAccent, "`") @@ -918,7 +1040,7 @@ proc accentedName(g: var TSrcGen, n: PNode) = gsub(g, n) proc infixArgument(g: var TSrcGen, n: PNode, i: int) = - if i < 1 and i > 2: return + if i < 1 or i > 2: return var needsParenthesis = false let nNext = n[i].skipHiddenNodes if nNext.kind == nkInfix: @@ -937,11 +1059,42 @@ proc infixArgument(g: var TSrcGen, n: PNode, i: int) = if needsParenthesis: put(g, tkParRi, ")") -proc gsub(g: var TSrcGen, n: PNode, c: TContext) = +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 - if n.comment.len > 0: pushCom(g, n) + 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 @@ -966,14 +1119,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 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 {nkStmtList, nkStmtListExpr}: + if n.len > 1 and n.lastSon.kind in postExprBlocks: accentedName(g, n[0]) - if n.len > 2: + 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, -2) + gcomma(g, n, 1, i - 1 - n.len) put(g, tkParRi, ")") - put(g, tkColon, ":") - gsub(g, n, n.len-1) + postStatements(g, n, i, fromStmtList) elif n.len >= 1: case bracketKind(g, n[0]) of bkBracket: @@ -1016,16 +1170,34 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"') else: gsub(g, n, 1) - of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: + 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") - put(g, tkBracketLe, "[") - gsub(g, n, 0) - put(g, tkBracketRi, "]") + 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, ")") @@ -1055,14 +1227,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkCommand: accentedName(g, n[0]) put(g, tkSpaces, Space) - if n[^1].kind == nkStmtList: - for i, child in n: - if i > 1 and i < n.len - 1: - put(g, tkComma, ",") - elif i == n.len - 1: - put(g, tkColon, ":") - if i > 0: - gsub(g, child) + 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: @@ -1070,6 +1240,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 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, "(") @@ -1086,9 +1261,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 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, tkParLe, "(") - gcomma(g, n, 1) put(g, tkParRi, ")") of nkClosedSymChoice, nkOpenSymChoice: if renderIds in g.flags: @@ -1106,6 +1281,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 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) @@ -1113,7 +1289,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkTupleConstr: put(g, tkParLe, "(") gcomma(g, n, c) - if n.len == 1: put(g, tkComma, ",") + if n.len == 1 and n[0].kind != nkExprColonExpr: put(g, tkComma, ",") put(g, tkParRi, ")") of nkCurly: put(g, tkCurlyLe, "{") @@ -1131,14 +1307,25 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gcomma(g, n, c) put(g, tkBracketRi, "]") of nkDotExpr: - gsub(g, n, 0) - put(g, tkDot, ".") - gsub(g, n, 1) + if isCustomLit(n): + put(g, tkCustomLit, n[0].strVal) + gsub(g, n, 1) + else: + gsub(g, n, 0) + put(g, tkDot, ".") + assert n.len == 2, $n.len + 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) @@ -1153,11 +1340,38 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n, pragmasPos) put(g, tkColon, ":") gsub(g, n, bodyPos) - of nkConstDef, nkIdentDefs: + 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, n.len - 2) + gsub(g, n[^2], c) + if n.len >= 1 and n[^1].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") @@ -1179,21 +1393,26 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 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 - # eg: `n1 == n2` decompses as following sum: + # 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: - if n.len > 0 and n[0].kind == nkIdent and n[0].ident.s == "<//>": - discard "XXX Remove this hack after 0.20 has been released!" - else: - gsub(g, n, 0) + 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 @@ -1208,9 +1427,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 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) - gsub(g, n, 0) + if renderNoPostfix notin g.flags: + gsub(g, n, 0) of nkRange: gsub(g, n, 0) put(g, tkDotDot, "..") @@ -1220,9 +1444,22 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkOpr, "[]") of nkAccQuoted: put(g, tkAccent, "`") - if n.len > 0: gsub(g, n[0]) - for i in 1..<n.len: - put(g, tkSpaces, Space) + 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: @@ -1263,6 +1500,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 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") @@ -1291,20 +1534,20 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkObjectTy: if n.len > 0: putWithSpace(g, tkObject, "object") - gsub(g, n[0]) - gsub(g, n[1]) - gcoms(g) - gsub(g, n[2]) + 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: - indentNL(g) for i in 0..<n.len: optNL(g) gsub(g, n[i], c) gcoms(g) - dedent(g) - putNL(g) of nkOfInherit: putWithSpace(g, tkOf, "of") gsub(g, n, 0) @@ -1344,7 +1587,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n, 1) - of nkStmtList, nkStmtListExpr, nkStmtListType: gstmts(g, n, emptyContext) + 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) @@ -1383,7 +1632,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkTypeSection: gsection(g, n, emptyContext, tkType, "type") of nkConstSection: - initContext(a) + a = initContext() incl(a.flags, rfInConstExpr) gsection(g, n, a, tkConst, "const") of nkVarSection, nkLetSection, nkUsingStmt: @@ -1403,7 +1652,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n[0]) of nkReturnStmt: putWithSpace(g, tkReturn, "return") - if n.len > 0 and n[0].kind == nkAsgn: + if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags: gsub(g, n[0], 1) else: gsub(g, n, 0) @@ -1551,13 +1800,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n, 0) put(g, tkParRi, ")") of nkGotoState: - var c: TContext - initContext c + var c: TContext = initContext() putWithSpace g, tkSymbol, "goto" gsons(g, n, c) of nkState: - var c: TContext - initContext c + var c: TContext = initContext() putWithSpace g, tkSymbol, "state" gsub(g, n[0], c) putWithSpace(g, tkColon, ":") @@ -1571,14 +1818,17 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 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, "rnimsyn.gsub(" & $n.kind & ')') + 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(g, renderFlags, newPartialConfigRef()) + var g: TSrcGen = initSrcGen(renderFlags, newPartialConfigRef()) # do not indent the initial statement list so that # writeFile("file.nim", repr n) # produces working Nim code: @@ -1590,14 +1840,13 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = proc `$`*(n: PNode): string = n.renderTree -proc renderModule*(n: PNode, infile, outfile: string, +proc renderModule*(n: PNode, outfile: string, renderFlags: TRenderFlags = {}; fid = FileIndex(-1); conf: ConfigRef = nil) = var - f: File - g: TSrcGen - initSrcGen(g, renderFlags, conf) + f: File = default(File) + g: TSrcGen = initSrcGen(renderFlags, conf) g.fid = fid for i in 0..<n.len: gsub(g, n[i]) @@ -1613,11 +1862,11 @@ proc renderModule*(n: PNode, infile, outfile: string, else: rawMessage(g.config, errGenerated, "cannot open file: " & outfile) -proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) = - initSrcGen(r, renderFlags, newPartialConfigRef()) - gsub(r, n) +proc initTokRender*(n: PNode, renderFlags: TRenderFlags = {}): TSrcGen = + result = initSrcGen(renderFlags, newPartialConfigRef()) + gsub(result, n) -proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) = +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 @@ -1632,15 +1881,3 @@ proc getTokSym*(r: TSrcGen): PSym = result = r.tokens[r.idx-1].sym else: result = nil - -proc quoteExpr*(a: string): string {.inline.} = - ## can be used for quoting expressions in error msgs. - "'" & a & "'" - -proc genFieldDefect*(field: PSym, disc: PSym): string = - ## this needs to be in a module accessible by jsgen, ccgexprs, and vm to - ## provide this error msg FieldDefect; msgs would be better but it does not - ## import ast - result = field.name.s.quoteExpr & " is not accessible using discriminant " & - disc.name.s.quoteExpr & " of type " & - disc.owner.name.s.quoteExpr |