no more code duplication bw liMessage and rawMessage + several bug fixes (#14415)

* no more code duplication bw rawMessage and liMessage; fixes some bugs in rawMessage
* all compiler messages work with hint:msgorigin
This commit is contained in:
Timothee Cour 2020-05-22 03:02:10 -07:00 • committed by GitHub
commit 0d0ea3a11e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 91 additions and 152 deletions

View file

@ -181,6 +181,12 @@ const
hintMin* = hintSuccess hintMin* = hintSuccess
hintMax* = high(TMsgKind) hintMax* = high(TMsgKind)
proc msgToStr*(msg: TMsgKind): string =
case msg
of warnMin..warnMax: WarningsToStr[ord(msg) - ord(warnMin)]
of hintMin..hintMax: HintsToStr[ord(msg) - ord(hintMin)]
else: "" # we could at least do $msg - prefix `err`
static: static:
doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1

View file

@ -345,12 +345,10 @@ proc mainCommand*(graph: ModuleGraph) =
var hints = newJObject() # consider factoring with `listHints` var hints = newJObject() # consider factoring with `listHints`
for a in hintMin..hintMax: for a in hintMin..hintMax:
let key = lineinfos.HintsToStr[ord(a) - ord(hintMin)] hints[a.msgToStr] = %(a in conf.notes)
hints[key] = %(a in conf.notes)
var warnings = newJObject() var warnings = newJObject()
for a in warnMin..warnMax: for a in warnMin..warnMax:
let key = lineinfos.WarningsToStr[ord(a) - ord(warnMin)] warnings[a.msgToStr] = %(a in conf.notes)
warnings[key] = %(a in conf.notes)
var dumpdata = %[ var dumpdata = %[
(key: "version", val: %VersionAsString), (key: "version", val: %VersionAsString),

View file

@ -125,7 +125,7 @@ proc compileSystemModule*(graph: ModuleGraph) =
proc wantMainModule*(conf: ConfigRef) = proc wantMainModule*(conf: ConfigRef) =
if conf.projectFull.isEmpty: if conf.projectFull.isEmpty:
fatal(conf, newLineInfo(conf, AbsoluteFile"command line", 1, 1), errGenerated, fatal(conf, newLineInfo(conf, AbsoluteFile(commandLineDesc), 1, 1), errGenerated,
"command expects a filename") "command expects a filename")
conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))

View file

@ -13,6 +13,13 @@ import
import std/private/miscdollars import std/private/miscdollars
type InstantiationInfo = typeof(instantiationInfo()) type InstantiationInfo = typeof(instantiationInfo())
template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
template flushDot(conf, stdorr) =
## safe to call multiple times
if conf.lastMsgWasDot:
conf.lastMsgWasDot = false
write(stdorr, "\n")
proc toCChar*(c: char; result: var string) = proc toCChar*(c: char; result: var string) =
case c case c
@ -37,7 +44,6 @@ proc makeCString*(s: string): Rope =
res.add('\"') res.add('\"')
result.add(rope(res)) result.add(rope(res))
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo = proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
result.fullPath = fullPath result.fullPath = fullPath
#shallow(result.fullPath) #shallow(result.fullPath)
@ -111,7 +117,6 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} = proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} =
result = newLineInfo(fileInfoIdx(conf, filename), line, col) result = newLineInfo(fileInfoIdx(conf, filename), line, col)
proc concat(strings: openArray[string]): string = proc concat(strings: openArray[string]): string =
var totalLen = 0 var totalLen = 0
for s in strings: totalLen += s.len for s in strings: totalLen += s.len
@ -147,6 +152,7 @@ const
# but column numbers start with 0, however most editors expect # but column numbers start with 0, however most editors expect
# first column to be 1, so we need to +1 here # first column to be 1, so we need to +1 here
ColOffset* = 1 ColOffset* = 1
commandLineDesc* = "command line"
proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L) proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
@ -162,9 +168,6 @@ proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
if i >=% conf.m.msgContext.len: result = unknownLineInfo if i >=% conf.m.msgContext.len: result = unknownLineInfo
else: result = conf.m.msgContext[i].info else: result = conf.m.msgContext[i].info
const
commandLineDesc = "command line"
template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string = template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
if fileIdx.int32 < 0 or conf == nil: if fileIdx.int32 < 0 or conf == nil:
(if fileIdx == commandLineIdx: commandLineDesc else: "???") (if fileIdx == commandLineIdx: commandLineDesc else: "???")
@ -277,11 +280,6 @@ type
msgSkipHook ## skip message hook even if it is present msgSkipHook ## skip message hook even if it is present
MsgFlags* = set[MsgFlag] MsgFlags* = set[MsgFlag]
template flushDot(stdorr) =
if conf.lastMsgWasDot:
write(stdorr, "\n")
conf.lastMsgWasDot = false
proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
## Writes given message string to stderr by default. ## Writes given message string to stderr by default.
## If ``--stdout`` option is given, writes to stdout instead. If message hook ## If ``--stdout`` option is given, writes to stdout instead. If message hook
@ -295,12 +293,12 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
conf.writelnHook(s) conf.writelnHook(s)
elif optStdout in conf.globalOptions or msgStdout in flags: elif optStdout in conf.globalOptions or msgStdout in flags:
if eStdOut in conf.m.errorOutputs: if eStdOut in conf.m.errorOutputs:
flushDot(stdout) flushDot(conf, stdout)
writeLine(stdout, s) writeLine(stdout, s)
flushFile(stdout) flushFile(stdout)
else: else:
if eStdErr in conf.m.errorOutputs: if eStdErr in conf.m.errorOutputs:
flushDot(stderr) flushDot(conf, stderr)
writeLine(stderr, s) writeLine(stderr, s)
# On Windows stderr is fully-buffered when piped, regardless of C std. # On Windows stderr is fully-buffered when piped, regardless of C std.
when defined(windows): when defined(windows):
@ -345,18 +343,18 @@ proc msgWrite(conf: ConfigRef; s: string) =
stderr stderr
write(stdOrr, s) write(stdOrr, s)
flushFile(stdOrr) flushFile(stdOrr)
conf.lastMsgWasDot = true # subsequent writes need `flushDot`
template styledMsgWriteln*(args: varargs[typed]) = template styledMsgWriteln*(args: varargs[typed]) =
if not isNil(conf.writelnHook): if not isNil(conf.writelnHook):
callIgnoringStyle(callWritelnHook, nil, args) callIgnoringStyle(callWritelnHook, nil, args)
elif optStdout in conf.globalOptions: elif optStdout in conf.globalOptions:
if eStdOut in conf.m.errorOutputs: if eStdOut in conf.m.errorOutputs:
flushDot(stdout) flushDot(conf, stdout)
callIgnoringStyle(writeLine, stdout, args) callIgnoringStyle(writeLine, stdout, args)
flushFile(stdout) flushFile(stdout)
else: elif eStdErr in conf.m.errorOutputs:
if eStdErr in conf.m.errorOutputs: flushDot(conf, stderr)
flushDot(stderr)
if optUseColors in conf.globalOptions: if optUseColors in conf.globalOptions:
callStyledWriteLineStderr(args) callStyledWriteLineStderr(args)
else: else:
@ -434,57 +432,6 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool = proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions
proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
var
title: string
color: ForegroundColor
kind: string
sev: Severity
case msg
of errMin..errMax:
sev = Severity.Error
writeContext(conf, unknownLineInfo)
title = ErrorTitle
color = ErrorColor
of warnMin..warnMax:
sev = Severity.Warning
if not conf.hasWarn(msg): return
writeContext(conf, unknownLineInfo)
title = if msg in conf.warningAsErrors: ErrorTitle else: WarningTitle
color = WarningColor
kind = WarningsToStr[ord(msg) - ord(warnMin)]
inc(conf.warnCounter)
of hintMin..hintMax:
sev = Severity.Hint
if not conf.hasHint(msg): return
title = HintTitle
color = HintColor
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(conf.hintCounter)
let s = msgKindToString(msg) % args
if conf.structuredErrorHook != nil:
conf.structuredErrorHook(conf, unknownLineInfo,
s & (if kind.len > 0: KindFormat % kind else: ""), sev)
if not ignoreMsgBecauseOfIdeTools(conf, msg):
if msg == hintProcessing:
msgWrite(conf, ".")
conf.lastMsgWasDot = true
else:
if conf.lastMsgWasDot:
msgWrite(conf, "\n")
conf.lastMsgWasDot = false
if kind.len > 0:
styledMsgWriteln(color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind))
else:
styledMsgWriteln(color, title, resetStyle, s)
handleError(conf, msg, doAbort, s)
proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
rawMessage(conf, msg, [arg])
proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) = proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) =
conf.m.fileInfos[fileIdx.int32].lines.add line conf.m.fileInfos[fileIdx.int32].lines.add line
@ -517,108 +464,111 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg) conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
eh: TErrorHandling, info2: InstantiationInfo) {.noinline.} = eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.noinline.} =
var var
title: string title: string
color: ForegroundColor color: ForegroundColor
kind: string
ignoreMsg = false ignoreMsg = false
sev: Severity sev: Severity
let kind = if msg != hintUserRaw: msg.msgToStr else: "" # xxx not sure why hintUserRaw is special
case msg case msg
of errMin..errMax: of errMin..errMax:
sev = Severity.Error sev = Severity.Error
writeContext(conf, info) writeContext(conf, info)
title = ErrorTitle title = ErrorTitle
color = ErrorColor color = ErrorColor
when false:
# we try to filter error messages so that not two error message # we try to filter error messages so that not two error message
# in the same file and line are produced: # in the same file and line are produced:
#ignoreMsg = lastError == info and eh != doAbort # xxx `lastError` is only used in this disabled code; but could be useful to revive
conf.m.lastError = info ignoreMsg = conf.m.lastError == info and info != unknownLineInfo and eh != doAbort
if info != unknownLineInfo: conf.m.lastError = info
of warnMin..warnMax: of warnMin..warnMax:
sev = Severity.Warning sev = Severity.Warning
ignoreMsg = not conf.hasWarn(msg) ignoreMsg = not conf.hasWarn(msg)
if not ignoreMsg: writeContext(conf, info) if not ignoreMsg: writeContext(conf, info)
title = if msg in conf.warningAsErrors: ErrorTitle else: WarningTitle title = if msg in conf.warningAsErrors: ErrorTitle else: WarningTitle
color = WarningColor color = WarningColor
kind = WarningsToStr[ord(msg) - ord(warnMin)]
inc(conf.warnCounter) inc(conf.warnCounter)
of hintMin..hintMax: of hintMin..hintMax:
sev = Severity.Hint sev = Severity.Hint
ignoreMsg = not conf.hasHint(msg) ignoreMsg = not conf.hasHint(msg)
title = HintTitle title = HintTitle
color = HintColor color = HintColor
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(conf.hintCounter) inc(conf.hintCounter)
let x = conf.toFileLineCol(info) & " "
let s = getMessageStr(msg, arg)
let s = if isRaw: arg else: getMessageStr(msg, arg)
if not ignoreMsg: if not ignoreMsg:
let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: ""
var kindmsg = if kind.len > 0: KindFormat % kind else: ""
if conf.structuredErrorHook != nil: if conf.structuredErrorHook != nil:
conf.structuredErrorHook(conf, info, s & (if kind.len > 0: KindFormat % kind else: ""), sev) conf.structuredErrorHook(conf, info, s & kindmsg, sev)
if not ignoreMsgBecauseOfIdeTools(conf, msg): if not ignoreMsgBecauseOfIdeTools(conf, msg):
if kind.len > 0: if msg == hintProcessing:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s, msgWrite(conf, ".")
KindColor, `%`(KindFormat, kind))
else: else:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg)
if conf.hasHint(hintSource): if conf.hasHint(hintSource) and info != unknownLineInfo:
conf.writeSurroundingSrc(info) conf.writeSurroundingSrc(info)
if conf.hasHint(hintMsgOrigin): if conf.hasHint(hintMsgOrigin):
styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle,
" compiler msg initiated here", KindColor, " compiler msg initiated here", KindColor,
KindFormat % HintsToStr[ord(hintMsgOrigin) - ord(hintMin)], KindFormat % hintMsgOrigin.msgToStr,
resetStyle) resetStyle)
handleError(conf, msg, eh, s) handleError(conf, msg, eh, s)
template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
let arg = msgKindToString(msg) % args
liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc(), isRaw = true)
template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc())
template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
# this fixes bug #7080 so that it is at least obvious 'fatal' # this fixes bug #7080 so that it is at least obvious 'fatal' was executed.
# was executed.
conf.m.errorOutputs = {eStdOut, eStdErr} conf.m.errorOutputs = {eStdOut, eStdErr}
const info2 = instantiationInfo(-1, fullPaths = true) liMessage(conf, info, msg, arg, doAbort, instLoc())
liMessage(conf, info, msg, arg, doAbort, info2)
template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
const info2 = instantiationInfo(-1, fullPaths = true) liMessage(conf, info, msg, arg, doRaise, instLoc())
liMessage(conf, info, msg, arg, doRaise, info2)
template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
const info2 = instantiationInfo(-1, fullPaths = true) liMessage(conf, info, errGenerated, arg, doRaise, instLoc())
liMessage(conf, info, errGenerated, arg, doRaise, info2)
template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
const info2 = instantiationInfo(-1, fullPaths = true) liMessage(conf, info, msg, arg, doNothing, instLoc())
liMessage(conf, info, msg, arg, doNothing, info2)
template localError*(conf: ConfigRef; info: TLineInfo, arg: string) = template localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
const info2 = instantiationInfo(-1, fullPaths = true) liMessage(conf, info, errGenerated, arg, doNothing, instLoc())
liMessage(conf, info, errGenerated, arg, doNothing, info2)
template localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) = template localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) =
const info2 = instantiationInfo(-1, fullPaths = true) liMessage(conf, info, errGenerated, format % params, doNothing, instLoc())
liMessage(conf, info, errGenerated, format % params, doNothing, info2)
template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
const info2 = instantiationInfo(-1, fullPaths = true) liMessage(conf, info, msg, arg, doNothing, instLoc())
liMessage(conf, info, msg, arg, doNothing, info2)
proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) = proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
const info2 = instantiationInfo(-1, fullPaths = true)
writeContext(conf, info) writeContext(conf, info)
liMessage(conf, info, errInternal, errMsg, doAbort, info2) liMessage(conf, info, errInternal, errMsg, doAbort, info2)
proc internalError*(conf: ConfigRef; errMsg: string) = template internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return internalErrorImpl(conf, info, errMsg, instLoc())
writeContext(conf, unknownLineInfo)
rawMessage(conf, errInternal, errMsg)
template assertNotNil*(conf: ConfigRef; e): untyped = template internalError*(conf: ConfigRef; errMsg: string) =
if e == nil: internalError(conf, $instantiationInfo()) internalErrorImpl(conf, unknownLineInfo, errMsg, instLoc())
e
template internalAssert*(conf: ConfigRef, e: bool) = template internalAssert*(conf: ConfigRef, e: bool) =
if not e: internalError(conf, $instantiationInfo()) # xxx merge with globalAssert from PR #14324
if not e:
const info2 = instLoc()
let arg = info2.toFileLineCol
internalErrorImpl(conf, unknownLineInfo, arg, info2)
template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) =
let m = "'$2' should be: '$1'" % [beau, got]
let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
liMessage(conf, info, msg, m, doNothing, instLoc())
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
if i.fileIndex.int32 < 0: if i.fileIndex.int32 < 0:
@ -628,25 +578,9 @@ proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
else: else:
result = conf.m.fileInfos[i.fileIndex.int32].quotedName result = conf.m.fileInfos[i.fileIndex.int32].quotedName
proc listWarnings*(conf: ConfigRef) = template listMsg(title, r) =
msgWriteln(conf, "Warnings:") msgWriteln(conf, title)
for warn in warnMin..warnMax: for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", a.msgToStr])
msgWriteln(conf, " [$1] $2" % [
if warn in conf.notes: "x" else: " ",
lineinfos.WarningsToStr[ord(warn) - ord(warnMin)]
])
proc listHints*(conf: ConfigRef) = proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
msgWriteln(conf, "Hints:") proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
for hint in hintMin..hintMax:
msgWriteln(conf, " [$1] $2" % [
if hint in conf.notes: "x" else: " ",
lineinfos.HintsToStr[ord(hint) - ord(hintMin)]
])
proc lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) =
let m = "'$2' should be: '$1'" % [beau, got]
if optStyleError in conf.globalOptions:
localError(conf, info, m)
else:
message(conf, info, hintName, m)

View file

@ -321,7 +321,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = newTypeS(tyTypeDesc, c) let typedesc = newTypeS(tyTypeDesc, c)
incl typedesc.flags, tfCheckedForDestructor incl typedesc.flags, tfCheckedForDestructor
typedesc.addSonSkipIntLit(assertNotNil(c.config, typ)) internalAssert(c.config, typ != nil)
typedesc.addSonSkipIntLit(typ)
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
c.config.options).linkTo(typedesc) c.config.options).linkTo(typedesc)
return newSymNode(sym, info) return newSymNode(sym, info)

View file

@ -121,9 +121,9 @@ proc applyFilter(p: var TParsers, n: PNode, filename: AbsoluteFile,
if f != filtNone: if f != filtNone:
assert p.config != nil assert p.config != nil
if p.config.hasHint(hintCodeBegin): if p.config.hasHint(hintCodeBegin):
rawMessage(p.config, hintCodeBegin, []) rawMessage(p.config, hintCodeBegin, "")
msgWriteln(p.config, result.s) msgWriteln(p.config, result.s)
rawMessage(p.config, hintCodeEnd, []) rawMessage(p.config, hintCodeEnd, "")
proc evalPipe(p: var TParsers, n: PNode, filename: AbsoluteFile, proc evalPipe(p: var TParsers, n: PNode, filename: AbsoluteFile,
start: PLLStream): PLLStream = start: PLLStream): PLLStream =