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
|
#
#
# 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
strutils, llstream, ast, idents, lexer, options, msgs, parser,
filters, filter_tmpl, renderer, lineinfos, pathutils
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
export Parser, parseAll, parseTopLevelStmt, 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] == '/'
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
openParser(p, filename, llStreamOpen(substr(line, i)), cache, config)
result = parseAll(p)
closeParser(p)
llStreamClose(s)
proc getFilter(ident: PIdent): FilterKind =
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:
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
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
if setupParser(p, fileIdx, cache, config):
result = parseAll(p)
closeParser(p)
|