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
|