Skip to content

Commit

Permalink
internal: use custom rendering for --expandArc (#813)
Browse files Browse the repository at this point in the history
## Summary

Report the output for `--expandArc` via manual `msgWrite` calls instead
of using `localReport` and use custom render-to-text logic for the
`PNode` tree. The rendering logic is kept simple, and while the
output of `--expandArc` stays largely the same, it doesn't represent
valid NimSkull code anymore.

This is a preparation for the introduction of a code-generator IR, as
with it, `astgen` is not going to output `PNode` AST anymore, meaning
that `renderTree` cannot be used there.

## Details

The `rsemExpandArc` report is removed and everything associated with it.
While it could be kept, the general direction it to move aways from
reports for compiler tracing, and so `msgWrite` is instead used. This
also helps with getting around cyclic imports once the new IR is
introduced.

The rendering logic is a simplified version of `renderTree`, with
more complex text-layouting and conditional logic removed. The supported
AST shapes are similar to that of the planned initial version of the
code-generator IR, and the routines are added to the new `cgirutils`
module.

In order for changes to the `expandArc`-using tests to stay small, the
rendered output is kept compatible with that of
`renderTree(n, {renderIr, renderNoComment})`, where reasonable.
  • Loading branch information
zerbina authored Jul 26, 2023
1 parent 963542e commit 61414fa
Show file tree
Hide file tree
Showing 11 changed files with 371 additions and 119 deletions.
1 change: 0 additions & 1 deletion compiler/ast/report_enums.nim
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,6 @@ type

rsemEffectsListingHint
rsemExpandMacro = "ExpandMacro" ## Trace macro expansion progress
rsemExpandArc = "ExpandArc"

rsemCompilesReport
rsemNonMatchingCandidates
Expand Down
2 changes: 1 addition & 1 deletion compiler/ast/reports_sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ type
of rsemReportTwoSym + rsemReportOneSym + rsemReportListSym:
symbols*: seq[PSym]

of rsemExpandMacro, rsemPattern, rsemExpandArc:
of rsemExpandMacro, rsemPattern:
expandedAst*: PNode

of rsemLockLevelMismatch, rsemMethodLockMismatch:
Expand Down
273 changes: 273 additions & 0 deletions compiler/backend/cgirutils.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
## Non-essential code-generator IR related routines.

import
std/[
strutils
],
compiler/ast/[
ast_types,
ast_query,
typesrenderer
],
compiler/utils/[
idioms
]

from compiler/ast/renderer import renderTree, TRenderFlag

type
RenderCtx = object
syms: seq[PSym]
## remembers the already-rendered symbols. Used to provide unique names.

proc disambiguate(c: var RenderCtx, s: PSym): int =
## Computes and returns a number to append to the symbol name in order to
## make it unique in the output. This way, multiple different symbols sharing
## the same name can be disambiguated.
result = 0
for it in c.syms.items:
if it == s:
return
elif it.name.id == s.name.id: # same name?
inc result

c.syms.add s # remember the symbol

proc render(c: var RenderCtx, ind: int, n: PNode, res: var string) =
template add(s: var string, n: PNode) =
render(c, ind, n, res)

template indent(extra = 1) =
if res.len > 0 and res[^1] == ' ':
# remove trailing space
res.setLen(res.len - 1)
res.add "\n"
res.add repeat(" ", ind + extra)

template newLine() =
indent(0)

template renderList(n: PNode, sep: untyped; start: int = 0; fin: int = 0) =
## Renders the items in the slice ``start..<n.len - fin``
for i in start..<n.len-fin:
if i > start:
sep
res.add n[i]

template renderList(n: PNode, sep: string; start: int = 0; fin: int = 0) =
## Renders the items in the slice ``start..<n.len - fin``
renderList(n, res.add(sep), start, fin)

case n.kind
of nkIntLit..nkInt64Lit:
res.addInt n.intVal
of nkCharLit, nkUIntLit..nkUInt64Lit:
res.addInt cast[BiggestUInt](n.intVal)
of nkFloatLit..nkFloat128Lit:
res.add $n.floatVal
of nkStrLiterals:
res.add '"'
res.add n.strVal
res.add '"'
of nkNilLit:
res.add "nil"
of nkNimNodeLit:
res.add "<ast>"
of nkSym:
res.add n.sym.name.s
let postfix = disambiguate(c, n.sym)
if postfix > 0 and n.sym.magic == mNone:
res.add "_" & $postfix

# the rendered code is currently used for the ``--expandArc``, so we also
# highlight cursor locals
if sfCursor in n.sym.flags:
res.add "_cursor"
of nkType:
if n.typ.sym != nil:
res.add $n.typ
else:
res.add "[type node]"
of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkHiddenStdConv, nkChckRange, nkChckRange64, nkChckRangeF:
res.add n[0]
of nkAddr:
res.add "addr "
res.add n[0]
of nkDerefExpr:
res.add n[0]
res.add "[]"
of nkDotExpr:
res.add n[0]
res.add '.'
res.add n[1]
of nkBracketExpr:
res.add n[0]
res.add '['
res.add n[1]
res.add ']'
of nkRange:
res.add n[0]
res.add ".."
res.add n[1]
of nkCast:
res.add "cast["
res.add $n[0].typ
res.add "]("
res.add n[1]
res.add ")"
of nkStringToCString:
res.add "cstring("
res.add n[0]
res.add ')'
of nkCStringToString:
res.add "string("
res.add n[0]
res.add ')'
of nkConv:
res.add n[0]
res.add '('
res.add n[1]
res.add ')'
of nkObjUpConv, nkObjDownConv:
res.add $n.typ
res.add "("
res.add n[0]
res.add ")"
of nkExprColonExpr:
res.add n[0]
res.add ": "
res.add n[1]
of nkCall:
res.add n[0]
res.add '('
let ind = ind + 1
renderList(n, ", ", 1)
res.add ')'

of nkObjConstr:
res.add $n[0].typ
res.add '('
renderList(n, ", ", 1)
res.add ')'
of nkTupleConstr, nkClosure:
res.add '('
renderList(n, ", ")
res.add ')'
of nkBracket:
res.add '['
renderList(n, ", ")
res.add ']'
of nkCurly:
res.add '{'
renderList(n, ", ")
res.add '}'

of nkAsgn, nkFastAsgn:
res.add n[0]
res.add " = "
let ind = ind + 1
res.add n[1]
of nkVarSection, nkLetSection:
for i, def in n.pairs:
if i > 0:
newLine()
res.add "var "
res.add def[0]
if def[2].kind != nkEmpty:
res.add " = "
let ind = ind + 1
res.add def[2]
of nkPragma:
# re-use the ``PNode`` rendering
res.add renderTree(n, {renderIr, renderNoComments})
of nkReturnStmt:
res.add "return"
of nkDiscardStmt:
res.add "discard "
res.add n[0]
of nkBreakStmt:
if n[0].kind == nkEmpty:
res.add "break"
else:
res.add "break "
res.add n[0]
of nkRaiseStmt:
if n[0].kind == nkEmpty:
res.add "raise"
else:
res.add "raise "
res.add n[0]
of nkAsmStmt:
res.add "asm "
let ind = ind + 1
renderList(n, ", ")
res.add ""
of nkWhileStmt:
res.add "while true:"
indent()
render(c, ind + 1, n[1], res)
of nkBlockStmt:
if n[0].kind == nkEmpty:
res.add "block:"
else:
res.add "block "
res.add n[0]
res.add ":"
indent()
render(c, ind + 1, n[1], res)
of nkIfStmt:
res.add "if "
res.add n[0][0]
res.add ':'
indent()
render(c, ind + 1, n[0][1], res)
of nkCaseStmt:
res.add "case "
res.add n[0]
for i in 1..<n.len:
newLine()
case n.kind
of nkOfBranch:
res.add "of "
renderList(n[i], ", ", 1, 1)
of nkElse:
res.add "else"
else:
unreachable()

res.add ":"
indent()
render(c, ind + 1, n[i][^1], res)
of nkTryStmt:
res.add "try:"
indent()
render(c, ind + 1, n[0], res)
for i in 1..<n.len:
case n[i].kind
of nkExceptBranch:
newLine()
res.add "except:"
indent()
render(c, ind + 1, n[i][^1], res)
of nkFinally:
newLine()
res.add "finally:"
indent()
render(c, ind + 1, n[i][0], res)
else:
unreachable()
of nkStmtListExpr:
newLine()
renderList(n, newLine())
of nkStmtList:
renderList(n, newLine())
of nkWithSons + nkWithoutSons - codegenExprNodeKinds -
{nkExprColonExpr, nkRange} + {nkEmpty}:
unreachable(n.kind)

proc render*(n: PNode): string =
## Renders `n` to human-readable code that tries to emulate the shape of the
## high-level language. The output is meant for debugging and tracing and is
## not guaranteed to have a stable format.
var c = RenderCtx()
render(c, 0, n, result)
12 changes: 0 additions & 12 deletions compiler/front/cli_reporter.nim
Original file line number Diff line number Diff line change
Expand Up @@ -520,17 +520,6 @@ proc reportBody*(conf: ConfigRef, r: SemReport): string =
r.typ.kind == tyProc:
result.add(" = ", typeToString(r.typ, preferDesc))


of rsemExpandArc:
result.add(
"--expandArc: ",
r.symstr,
"\n",
r.expandedAst.renderTree({renderIr, renderNoComments}),
"\n",
"-- end of expandArc ------------------------"
)

of rsemCannotBorrow:
result.add(
"cannot borrow ",
Expand Down Expand Up @@ -2238,7 +2227,6 @@ proc reportBody*(conf: ConfigRef, r: SemReport): string =


const standalone = {
rsemExpandArc, # Original compiler did not consider it as a hint
rvmStackTrace, # Always associated with extra report
rsemDiagnostics, # Wraps other reports
}
Expand Down
7 changes: 1 addition & 6 deletions compiler/front/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -708,9 +708,6 @@ type
writeForceEnabled

func writabilityKind*(conf: ConfigRef, r: Report): ReportWritabilityKind =
const forceWrite =
{rsemExpandArc} # Not a hint, just legacy reports overeach

let compTimeCtx = conf.m.errorOutputs == {}
## indicates whether we're in a `compiles` or `constant expression
## evaluation` context. `sem` and `semexprs` in particular will clear
Expand Down Expand Up @@ -743,9 +740,7 @@ func writabilityKind*(conf: ConfigRef, r: Report): ReportWritabilityKind =

elif (
# Not explicitly enabled
not conf.isEnabled(r) and
# And not added for forced write
r.kind notin forceWrite
not conf.isEnabled(r)
) or (
# Or we are in the special hack mode for `compiles()` processing
compTimeCtx
Expand Down
19 changes: 11 additions & 8 deletions compiler/sem/injectdestructors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ import
from compiler/ast/reports_sem import SemReport
from compiler/ast/report_enums import ReportKind

# XXX: we shouldn't need to be concerned with rendering backend-
# IR to text here
from compiler/backend/cgirutils import render

type
AnalyseCtx = object
cfg: ControlFlowGraph
Expand Down Expand Up @@ -1317,12 +1321,11 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym;
apply(changes)

if g.config.arcToExpand.hasKey(owner.name.s):
# the diagnostic expects a ``PNode`` AST, so we first have to tranlsate
# the MIR code into one. While this is inefficient, ``expandArc`` is only
# meant as a utility, so it's okay for now.
# due to some parts of it being very declarative, rendering and echoing
# the MIR code wouldn't be very useful, so we turn it into backend IR
# first, which we then render to text
# XXX: this needs a deeper rethink
let n = generateAST(g, idgen, owner, tree, sourceMap)
g.config.localReport(SemReport(
kind: rsemExpandArc,
sym: owner,
expandedAst: n
))
g.config.msgWrite("--expandArc: " & owner.name.s & "\n")
g.config.msgWrite(render(n))
g.config.msgWrite("\n-- end of expandArc ------------------------\n")
Loading

0 comments on commit 61414fa

Please sign in to comment.