1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Implements the dispatcher for the different parsers.
import
llstream, ast, idents, lexer, options, msgs, parser,
filters, filter_tmpl, renderer, lineinfos, pathutils
import std/strutils
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
export Parser, parseAll, parseTopLevelStmt, checkFirstLineIndentation, closeParser
type
FilterKind = enum
filtNone = "none"
filtTemplate = "stdtmpl"
filtReplace = "replace"
filtStrip = "strip"
proc utf8Bom(s: string): int =
if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
3
else:
0
proc containsShebang(s: string, i: int): bool =
if i+1 < s.len and s[i] == '#' and s[i+1] == '!':
var j = i + 2
while j < s.len and s[j] in Whitespace: inc(j)
result = s[j] == '/'
else:
result = false
proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache;
config: ConfigRef): PNode =
result = newNode(nkEmpty)
var s = llStreamOpen(filename, fmRead)
if s != nil:
var line = newStringOfCap(80)
discard llStreamReadLine(s, line)
var i = utf8Bom(line)
var linenumber = 1
if containsShebang(line, i):
discard llStreamReadLine(s, line)
i = 0
inc linenumber
if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
when defined(nimpretty):
# XXX this is a bit hacky, but oh well...
config.quitOrRaise "can't nimpretty a source code filter: " & $filename
else:
inc(i, 2)
while i < line.len and line[i] in Whitespace: inc(i)
var p: Parser = default(Parser)
openParser(p, filename, llStreamOpen(substr(line, i)), cache, config)
result = parseAll(p)
closeParser(p)
llStreamClose(s)
proc getFilter(ident: PIdent): FilterKind =
result = filtNone
for i in FilterKind:
if cmpIgnoreStyle(ident.s, $i) == 0:
return i
proc getCallee(conf: ConfigRef; n: PNode): PIdent =
if n.kind in nkCallKinds and n[0].kind == nkIdent:
result = n[0].ident
elif n.kind == nkIdent:
result = n.ident
else:
result = nil
localError(conf, n.info, "invalid filter: " & renderTree(n))
proc applyFilter(p: var Parser, n: PNode, filename: AbsoluteFile,
stdin: PLLStream): PLLStream =
var f = getFilter(getCallee(p.lex.config, n))
result = case f
of filtNone:
stdin
of filtTemplate:
filterTmpl(p.lex.config, stdin, filename, n)
of filtStrip:
filterStrip(p.lex.config, stdin, filename, n)
of filtReplace:
filterReplace(p.lex.config, stdin, filename, n)
if f != filtNone:
assert p.lex.config != nil
if p.lex.config.hasHint(hintCodeBegin):
rawMessage(p.lex.config, hintCodeBegin, "")
msgWriteln(p.lex.config, result.s)
rawMessage(p.lex.config, hintCodeEnd, "")
proc evalPipe(p: var Parser, n: PNode, filename: AbsoluteFile,
start: PLLStream): PLLStream =
assert p.lex.config != nil
result = start
if n.kind == nkEmpty: return
if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|":
for i in 1..2:
if n[i].kind == nkInfix:
result = evalPipe(p, n[i], filename, result)
else:
result = applyFilter(p, n[i], filename, result)
elif n.kind == nkStmtList:
result = evalPipe(p, n[0], filename, result)
else:
result = applyFilter(p, n, filename, result)
proc openParser*(p: var Parser, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef) =
assert config != nil
let filename = toFullPathConsiderDirty(config, fileIdx)
var pipe = parsePipe(filename, inputstream, cache, config)
p.lex.config = config
let s = if pipe != nil: evalPipe(p, pipe, filename, inputstream)
else: inputstream
parser.openParser(p, fileIdx, s, cache, config)
proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache;
config: ConfigRef): bool =
let filename = toFullPathConsiderDirty(config, fileIdx)
var f: File = default(File)
if not open(f, filename.string):
rawMessage(config, errGenerated, "cannot open file: " & filename.string)
return false
openParser(p, fileIdx, llStreamOpen(f), cache, config)
result = true
proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode =
var p: Parser = default(Parser)
if setupParser(p, fileIdx, cache, config):
result = parseAll(p)
closeParser(p)
else:
result = nil
|