aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--c/lex.c85
-rw-r--r--common.h5
-rw-r--r--io.c75
3 files changed, 137 insertions, 28 deletions
diff --git a/c/lex.c b/c/lex.c
index 03847cf..e43acbb 100644
--- a/c/lex.c
+++ b/c/lex.c
@@ -895,6 +895,7 @@ static void
expecteol(struct lexer *lx, const char *ppname)
{
struct token tk;
+ assert(!lx->macstk);
if (lex0(lx, &tk) != '\n' && tk.t != TKEOF) {
(ccopt.pedant ? error : warn)(&tk.span, "extra tokens after #%s", ppname);
ppskipline(lx);
@@ -1580,7 +1581,7 @@ ppinclude(struct lexer *lx, const struct span *span0)
if (tk.s[0] == '/') goto NotFound;
/* try relative to current file's directory */
- base = getfilename(lx->fileid);
+ base = getfilename(lx->fileid, 0);
for (end = base; *end != 0; ++end) {}
for (--end; *end != '/' && end != base; --end) {}
if (*end == '/') ++end;
@@ -1616,6 +1617,68 @@ ppinclude(struct lexer *lx, const struct span *span0)
}
static void
+ppline(struct lexer *lx, struct token *tk0)
+{
+ struct token tk, tks[2];
+ int ntk = 0;
+ struct span span = tk0->span;
+ bool ext = 0;
+ if (tk0->t == TKNUMLIT) { /* handles GNU-style post preprocessing directive '# n ...' */
+ tks[ntk++] = *tk0;
+ ext = 1;
+ }
+ while (ntk < 2) {
+ if (lx->macstk && advancemacro(lx, &tk)) {
+ tks[ntk++] = tk;
+ if (lx->macstk->idx >= lx->macstk->rlist.n) popmac(lx);
+ } else if (!lx->macstk && (lex0(lx, &tk) == '\n' || tk.t == TKEOF)) {
+ break;
+ } else if (isppident(tk) && tryexpand(lx, &tk)) {
+ continue;
+ } else {
+ tks[ntk++] = tk;
+ }
+ }
+ uvlong lineno = 0;
+ char *file = NULL;
+ if (ntk > 0 && tks[0].t == TKNUMLIT) {
+ if (!parsenumlit(&lineno, NULL, &tks[0], 1) || (lineno == 0 && !ext))
+ goto BadNum;
+ if (lineno >= 1<<(32-SPANFILEBITS)) {
+ warn(&tks[0].span, "ignoring #line number that is too big");
+ lineno = 0;
+ goto Err;
+ }
+ } else {
+ BadNum:
+ error(ntk ? &tks[0].span : &span, "#line requires a positive integer argument");
+ Err:
+ if (lx->macstk || (tk.t != '\n' && tk.t != TKEOF)) ppskipline(lx);
+ return;
+ }
+ if (ntk > 1) {
+ if (tks[1].t == TKSTRLIT && !tks[1].wide) {
+ file = alloc(&globarena, tks[1].len+1, 0);
+ memcpy(file, tks[1].s, tks[1].len);
+ file[tks[1].len] = 0;
+ } else {
+ error(&tks[1].span, "invalid filename for #line directive");
+ }
+ }
+ if (lineno) setfileline(lx->fileid, lx->chridx, lineno, file);
+ if (lx->macstk) {
+ span.sl.off = span.ex.off = lx->chridx;
+ span.sl.len = span.ex.len = 1;
+ ppskipline(lx);
+ if (!ext)
+ (ccopt.pedant ? error : warn)(&span, "extra tokens after #line");
+ } else if (tk.t != '\n' && tk.t != TKEOF) {
+ if (ext) ppskipline(lx);
+ else expecteol(lx, "line");
+ }
+}
+
+static void
pppragma(struct lexer *lx, const struct span *span0)
{
struct token tk;
@@ -1726,8 +1789,8 @@ lex(struct lexer *lx, struct token *tk_)
while ((t = lex0(lx, tk)) == '\n') linebegin = 1;
if (t == '#' && linebegin) {
if (lex0(lx, tk) == '\n') { }
- else if (isppident(*tk)) {
- lastcmd = findppcmd(tk);
+ else if (tk->t == TKNUMLIT || isppident(*tk)) {
+ lastcmd = tk->t == TKNUMLIT ? PPLINE : findppcmd(tk);
if (nppcnd == lx->nppcnd0) lx->inclguard = NULL;
if (!skip) {
switch (lastcmd) {
@@ -1743,7 +1806,7 @@ lex(struct lexer *lx, struct token *tk_)
case PPELSE: ppelse(lx, &tk->span); break;
case PPENDIF: ppendif(lx, &tk->span); break;
case PPINCLUDE: ppinclude(lx, &tk->span); break;
- case PPLINE: break;
+ case PPLINE: ppline(lx, tk); break;
case PPPRAGMA: pppragma(lx, &tk->span); break;
case PPWARNING: ppdiag(lx, &tk->span, 0); break;
case PPERROR: ppdiag(lx, &tk->span, 1); break;
@@ -1845,7 +1908,7 @@ static void
mac__file__handler(struct lexer *lx, struct token *tk)
{
tk->t = TKSTRLIT;
- tk->s = getfilename(lx->fileid);
+ tk->s = getfilename(lx->fileid, lx->chridx);
tk->wide = 0;
tk->len = strlen(tk->s);
}
@@ -2011,7 +2074,7 @@ initlexer(struct lexer *lx, const char **err, const char *file)
lx->chrbuf0 = countof(lx->chrbuf);
lx->firstdirective = 1;
lx->nppcnd0 = nppcnd;
- return getfilename(fileid) != file ? LXFILESEEN : LXOK;
+ return getfilename(fileid, 0) != file ? LXFILESEEN : LXOK;
}
/* callback to let lexer release temp memory for arena allocated token data */
@@ -2029,15 +2092,17 @@ lexerdump(struct lexer *lx, struct wbuf *out)
{
struct token prev = {0}, tok;
int file = lx->fileid, line = 1, col = 1;
- bfmt(out, "# %d %'s\n", 1, getfilename(file));
+ const char *lastfile = getfilename(file, 0);
+ bfmt(out, "# %d %'s\n", 1, lastfile);
while (lex(lx, &tok) != TKEOF) {
int tkline, tkcol;
- getfilepos(&tkline, &tkcol, tok.span.ex.file, tok.span.ex.off);
- if (tok.span.ex.file != file) {
+ const char *fname = getfilepos(&tkline, &tkcol, tok.span.ex.file, tok.span.ex.off);
+ if (tok.span.ex.file != file || fname != lastfile) {
file = tok.span.ex.file;
- bfmt(out, "\n# %d %'s\n", tkline, getfilename(file));
+ bfmt(out, "\n# %d %'s\n", tkline, fname);
col = 1;
lexerfreetemps(lx);
+ lastfile = fname;
} else if (line < tkline && tkline - line < 5) {
do
ioputc(out, '\n');
diff --git a/common.h b/common.h
index 53669f4..0d1ec4c 100644
--- a/common.h
+++ b/common.h
@@ -396,10 +396,11 @@ void *mapzeros(uint);
int munmap(void *, size_t);
int getpredeffile(struct memfile **, const char *name);
int openfile(const char **err, struct memfile **, const char *path);
-const char *getfilename(int id);
+const char *getfilename(int id, uint atoff);
struct memfile *getfile(int id);
void addfileline(int id, uint off);
-void getfilepos(int *line, int *col, int id, uint off);
+void setfileline(int id, uint off, int line, const char *file);
+const char *getfilepos(int *line, int *col, int id, uint off);
bool isoncefile(int id, const char **guard);
void markfileonce(int id, const char *guard);
void markfileseen(int id);
diff --git a/io.c b/io.c
index 895f204..529710b 100644
--- a/io.c
+++ b/io.c
@@ -794,11 +794,19 @@ struct fileuid {
long dev, ino;
};
+/* one entry per #line */
+struct linemap {
+ int phys;
+ int toline;
+ const char *tofile;
+};
+
static struct file {
struct fileuid uid;
const char *path;
struct memfile f;
vec_of(uint) lineoffs;
+ vec_of(struct linemap) linemap;
bool once;
bool seen;
const char *guardmac;
@@ -880,10 +888,12 @@ openfile(const char **err, struct memfile **pf, const char *path)
}
const char *
-getfilename(int id)
+getfilename(int id, uint atoff)
{
assert((uint)id < countof(fileht) && fileht[id]);
- return fileht[id]->path;
+ if (!fileht[id]->linemap.n || !atoff)
+ return fileht[id]->path;
+ return getfilepos(NULL, NULL, id, atoff);
}
struct memfile *
@@ -896,26 +906,40 @@ getfile(int id)
void
addfileline(int id, uint off)
{
- vec_of(uint) *lineoffs;
-
assert((uint)id < countof(fileht) && fileht[id]);
- lineoffs = (void *)&fileht[id]->lineoffs;
+ vec_of(uint) *lineoffs = (void *)&fileht[id]->lineoffs;
if (lineoffs->n && off > lineoffs->p[lineoffs->n-1])
vpush(lineoffs, off);
}
void
-getfilepos(int *line, int *col, int id, uint off)
+setfileline(int id, uint off, int line, const char *file)
{
- uint *offs, n;
- int l = 0, h, i = 0;
+ assert((uint)id < countof(fileht) && fileht[id]);
+ vec_of(struct linemap) *linemap = (void *)&fileht[id]->linemap;
+ vec_of(uint) *lineoffs = (void *)&fileht[id]->lineoffs;
+ int phys = 2;
+ for (int i = lineoffs->n-1; i >= 0; --i) {
+ if (lineoffs->p[i] < off) {
+ phys = i+2;
+ break;
+ }
+ }
+ if (linemap->n > 0) {
+ assert(linemap->p[linemap->n-1].phys < phys);
+ if (!file) file = linemap->p[linemap->n-1].tofile;
+ }
+ vpush(linemap, ((struct linemap){ phys, line, file }));
+}
+const char *
+getfilepos(int *pline, int *pcol, int id, uint off)
+{
assert((uint)id < countof(fileht) && fileht[id]);
- offs = fileht[id]->lineoffs.p;
- n = fileht[id]->lineoffs.n;
- h = n - 1;
-
+ uint *offs = fileht[id]->lineoffs.p;
+ uint n = fileht[id]->lineoffs.n;
/* binary search over offsets array */
+ int l = 0, h = n - 1, i = 0;
while (l <= h) {
i = (l + h) / 2;
if (offs[i] < off) l = i + 1;
@@ -923,8 +947,27 @@ getfilepos(int *line, int *col, int id, uint off)
else break;
}
i -= offs[i] > off;
- if (line) *line = i + 1;
- if (col) *col = off - offs[i] + 1;
+ int line = i + 1, col = off - offs[i] + 1;
+ const char *file = fileht[id]->path;
+ vec_of(struct linemap) *linemap = (void *)&fileht[id]->linemap;
+ if (linemap->n) {
+ /* binary search over linemap array */
+ l = 0, h = linemap->n - 1, i = 0;
+ while (l <= h) {
+ i = (l + h) / 2;
+ if (linemap->p[i].phys < line) l = i + 1;
+ else if (linemap->p[i].phys > line) h = i - 1;
+ else break;
+ }
+ i -= linemap->p[i].phys > line;
+ if (i >= 0) {
+ line = linemap->p[i].toline + (line - linemap->p[i].phys);
+ if (linemap->p[i].tofile) file = linemap->p[i].tofile;
+ }
+ }
+ if (pline) *pline = line;
+ if (pcol) *pcol = col;
+ return file;
}
bool
@@ -978,8 +1021,8 @@ vdiag(const struct span *span, enum diagkind kind, const char *fmt, va_list ap)
if (span) {
loc = span->ex.len ? &span->ex : &span->sl;
f = getfile(loc->file);
- getfilepos(&line, &col, loc->file, loc->off);
- efmt("%s:%d:%d: ", getfilename(loc->file), line, col);
+ const char *file = getfilepos(&line, &col, loc->file, loc->off);
+ efmt("%s:%d:%d: ", file, line, col);
}
efmt(color[kind]);
efmt("%s: %g.", label[kind]);