Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testament diff rework - unstructured, structured #206

Merged
merged 1 commit into from
Feb 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions compiler/front/cli_reporter.nim
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ proc toStr(conf: ConfigRef, loc: TLineInfo, dropExt: bool = false): string =
## Convert location to printable string
conf.wrap(
"$1($2, $3)" % [
toFilenameOption(conf, loc.fileIndex, conf.filenameOption).dropExt(dropExt),
conf.toMsgFilename(loc.fileIndex).dropExt(dropExt),
$loc.line,
$(loc.col + ColOffset)
],
Expand Down Expand Up @@ -3120,7 +3120,12 @@ proc reportBody*(conf: ConfigRef, r: ExternalReport): string =
result = "$1 is not a valid number" % r.cmdlineProvided

of rextInvalidValue:
result = r.cmdlineError
result = ("Unexpected value for " &
"the $1. Expected one of $2, but got '$3'") % [
r.cmdlineSwitch,
r.cmdlineAllowed.mapIt("'" & it & "'").join(", "),
r.cmdlineProvided
]

of rextUnexpectedValue:
result = "Unexpected value for $1. Expected one of $2" % [
Expand Down
16 changes: 15 additions & 1 deletion compiler/front/commands.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ import
front/[
condsyms,
options,
msgs
msgs,
cli_reporter,
sexp_reporter
],
backend/[
extccomp
Expand Down Expand Up @@ -1121,6 +1123,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
else:
conf.localReport(info, invalidSwitchValue @["abs", "canonical", "legacyRelProj"])

of "msgformat":
case arg.normalize:
of "text":
conf.setReportHook cli_reporter.reportHook

of "sexp":
conf.setReportHook sexp_reporter.reportHook

else:
conf.localReport(info, invalidSwitchValue @["text", "sexp"])

of "processing":
incl(conf, cnCurrent, rsemProcessing)
incl(conf, cnMainPackage, rsemProcessing)
Expand Down Expand Up @@ -1269,6 +1282,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "nilseqs", "nilchecks", "mainmodule", "m", "symbol", "taintmode",
"cs", "deadcodeelim":
warningOptionNoop(switch)

else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
else: invalidCmdLineOption(conf, pass, switch, info)
Expand Down
1 change: 0 additions & 1 deletion compiler/front/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,6 @@ type
) {.closure.} ## All
## textual output from the compiler goes through this callback.
writeHook*: proc(conf: ConfigRef, output: string, flags: MsgFlags) {.closure.}

structuredReportHook*: ReportHook
cppCustomNamespace*: string
vmProfileData*: ProfileData
Expand Down
182 changes: 182 additions & 0 deletions compiler/front/sexp_reporter.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
## Implementation of the structured CLI message generator. Using
## `--msgFormat=sexp` will make compiler switch to the report hook
## implemented in this module.

import
experimental/[
sexp,
diff,
colortext,
sexp_diff
],
ast/[
lineinfos,
ast,
reports
],
front/[
options,
msgs
],
std/[
strutils
]

import std/options as std_options

var writeConf: ConfigRef


proc addFields[T](s: var SexpNode, r: T, ignore: seq[string] = @[])



proc sexpItems*[T](s: T): SexpNode =
Copy link
Collaborator

@saem saem Feb 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this below the other s-exp procs, it flows better and if the T is one of the ones below it'll not be confusing if some declares a new one but this doesn't take it into account up here.

Also, seriously this probably shouldn't compile.

Copy link
Collaborator Author

@haxscramper haxscramper Feb 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, seriously this process shouldn't compile.

what 'process' are you talking about and why it shouldn't compile?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo, yeah it's strange that it's allowed by the compiler.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's strange that it's allowed by the compiler.

again, why?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because a type T is not guaranteed to have an items, yes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't compile when I instantiate the proc like every other generic, so I guess this is not allowed? I just don't get why this remark is attached to a definition. sexp two lines above can have $ defined as {.error.} and it would've been an instantiation error as well

result = newSList()
for item in items(s):
result.add sexp(item)

proc sexp*[T: object | tuple](obj: T): SexpNode =
result = newSList()
addFields(result, obj)

proc sexp*[T: object | tuple](obj: ref T): SexpNode = sexp(obj[])
proc sexp*[E: enum](e: E): SexpNode = newSSymbol($e)
proc sexp*[T](s: seq[T]): SexpNode = sexpItems(s)
proc sexp*[R, T](s: array[R, T]): SexpNode = sexpItems(s)
proc sexp*[I](s: set[I]): SexpNode = sexpItems(s)
proc sexp*(s: cstring): SexpNode = sexp($s)

proc sexp*(v: SomeInteger): SexpNode = newSInt(BiggestInt(v))
proc sexp*(id: FileIndex): SexpNode =
sexp(writeConf.toMsgFilename(id))


iterator sexpFields[T: object | tuple](obj: T, ignore: seq[string] = @[]): SexpNode =
for name, value in fieldPairs(obj):
var pass = true
when value is ref or value is ptr:
if isNil(value):
pass = false

when value is seq or value is string:
if len(value) == 0:
pass = false

when value is TLineInfo:
if pass and value == unknownLineInfo:
pass = false

when value is ReportLineInfo:
if pass and not value.isValid():
pass = false

if pass and name in ignore:
pass = false

if pass:
yield newSKeyword(name, sexp(value))


proc add*(self: var SexpNode, str: string, expr: SexpNode) =
self.add newSSymbol(":" & str)
self.add expr

proc sexp*[T](o: Option[T]): SexpNode =
if o.isNone: newSNil() else: sexp(o.get())
haxscramper marked this conversation as resolved.
Show resolved Hide resolved

proc addFields[T](s: var SexpNode, r: T, ignore: seq[string] = @[]) =
for item in sexpFields(r, ignore):
s.add item

proc sexp*(i: ReportLineInfo): SexpNode =
convertSexp([
writeConf.formatPath(i.file).sexp(),
sexp(i.line),
sexp(i.col)
])

proc sexp*(i: TLineInfo): SexpNode =
convertSexp([sexp(i.fileIndex), sexp(i.line), sexp(i.col)])

proc sexp*(e: StackTraceEntry): SexpNode =
result = newSList()
result.addFields(e, @["filename"])
result.add newSKeyword(
"filename", writeConf.formatPath($e.filename).sexp())


proc sexp*(typ: PType): SexpNode =
if typ.isNil: return newSNil()
result = newSList()
result.add newSSymbol(($typ.kind)[2 ..^ 1])
if typ.sons.len > 0:
result.add("sons", sexp(typ.sons))

proc sexp*(node: PNode): SexpNode =
if node.isNil: return newSNil()

result = newSList()
result.add newSSymbol(($node.kind)[2 ..^ 1])
case node.kind:
of nkCharLit..nkUInt64Lit: result.add sexp(node.intVal)
of nkFloatLit..nkFloat128Lit: result.add sexp(node.floatVal)
of nkStrLit..nkTripleStrLit: result.add sexp(node.strVal)
of nkSym: result.add newSSymbol(node.sym.name.s)
of nkIdent: result.add newSSymbol(node.ident.s)
else:
for node in node.sons:
result.add sexp(node)

proc sexp*(t: PSym): SexpNode =
convertSexp([
substr($t.kind, 2).newSSymbol(),
name = sexp(t.name.s),
info = sexp(t.info)
])


proc reportHook*(conf: ConfigRef, r: Report): TErrorHandling =
writeConf = conf
let wkind = conf.writabilityKind(r)

if wkind == writeDisabled:
return

else:
var s = newSList()
s.add newSSymbol(multiReplace($r.kind, {
"rsem": "Sem",
"rpar": "Par",
"rlex": "Lex",
"rint": "Int",
"rext": "Ext",
"rdbg": "Dbg",
"rback": "Bck",
}))
s.add newSSymbol(":severity")
s.add sexp(conf.severity(r))

let f = @["kind"]

case r.category:
of repLexer: s.addFields(r.lexReport, f)
of repParser: s.addFields(r.parserReport, f)
of repCmd: s.addFields(r.cmdReport, f)
of repSem:
if r.kind == rsemProcessingStmt:
s.addFields(r.semReport, f & "node")

else:
s.addFields(r.semReport, f)

of repDebug: s.addFields(r.debugReport, f)
of repInternal: s.addFields(r.internalReport, f)
of repBackend: s.addFields(r.backendReport, f)
of repExternal: s.addFields(r.externalReport, f)

if wkind == writeForceEnabled:
echo s.toLine().toString(conf.useColor)

else:
conf.writeln(s.toLine().toString(conf.useColor))
1 change: 1 addition & 0 deletions doc/advopt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Advanced options:
to after all options have been processed
--stdout:on|off output to stdout
--colors:on|off turn compiler messages coloring on|off
--msgFormat:text|sexp Select compiler message format - text or S-expressions
--filenames:abs|canonical|legacyRelProj
customize how filenames are rendered in compiler messages,
defaults to `abs` (absolute)
Expand Down
14 changes: 14 additions & 0 deletions doc/nimc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,20 @@ Level Description
for compiler developers.
===== ============================================

Compiler message formats
------------------------

The compiler can output messages in both unstructured (plaintext) and
structured (S-expressions) forms. S-expressions were chosen mostly for
integration with testament, in the future json support will be added as
well.

You can select message format using `--msgFormat=text|sexp`:option: switch
in the compiler. Unstructured compiler reports are formatted for higher
readability and used by default. Structured reports are formatted as
S-expressions, one per line. Every single compiler report is wrapped in
structured data, including ``echo`` messages at compile-time.


Compile-time symbols
--------------------
Expand Down
Loading