codemap.rs 5.5 KB
Newer Older
1 2
import core::{vec, uint, str, option, result};
import option::{some, none};
3

B
Brian Anderson 已提交
4
type filename = str;
5

M
Marijn Haverbeke 已提交
6
type file_pos = {ch: uint, byte: uint};
7

8 9 10 11 12
/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */
M
Marijn Haverbeke 已提交
13
type filemap =
14 15
    @{name: filename, src: @str,
      start_pos: file_pos, mutable lines: [file_pos]};
16

17
type codemap = @{mutable files: [filemap]};
18

19
type loc = {file: filemap, line: uint, col: uint};
20

21 22 23
fn new_codemap() -> codemap {
    @{mutable files: [new_filemap("-", @"", 0u, 0u)]}
}
24

25 26
fn new_filemap(filename: filename, src: @str,
               start_pos_ch: uint, start_pos_byte: uint)
M
Marijn Haverbeke 已提交
27
   -> filemap {
28
    ret @{name: filename, src: src,
M
Marijn Haverbeke 已提交
29
          start_pos: {ch: start_pos_ch, byte: start_pos_byte},
B
Brian Anderson 已提交
30
          mutable lines: [{ch: start_pos_ch, byte: start_pos_byte}]};
31 32
}

33 34
fn empty_filemap(cm: codemap) -> filemap {cm.files[0]}

M
Marijn Haverbeke 已提交
35
fn next_line(file: filemap, chpos: uint, byte_pos: uint) {
B
Brian Anderson 已提交
36
    file.lines += [{ch: chpos, byte: byte_pos}];
37 38
}

39
type lookup_fn = fn@(file_pos) -> uint;
40

41 42 43
fn lookup_line(map: codemap, pos: uint, lookup: lookup_fn)
    -> option::t<{fm: filemap, line: uint}>
{
44
    let len = vec::len(map.files);
M
Marijn Haverbeke 已提交
45
    let a = 0u;
46
    let b = len;
M
Marijn Haverbeke 已提交
47 48
    while b - a > 1u {
        let m = (a + b) / 2u;
B
Brian Anderson 已提交
49
        if lookup(map.files[m].start_pos) > pos { b = m; } else { a = m; }
50
    }
51
    if (a >= len) {
52
        ret none;
53
    }
B
Brian Anderson 已提交
54
    let f = map.files[a];
55
    a = 0u;
B
Brian Anderson 已提交
56
    b = vec::len(f.lines);
M
Marijn Haverbeke 已提交
57 58
    while b - a > 1u {
        let m = (a + b) / 2u;
B
Brian Anderson 已提交
59
        if lookup(f.lines[m]) > pos { b = m; } else { a = m; }
60
    }
61 62 63 64 65 66
    ret some({fm: f, line: a});
}

fn lookup_pos(map: codemap, pos: uint, lookup: lookup_fn) -> loc {
    alt lookup_line(map, pos, lookup) {
      some({fm: f, line: a}) {
67
        {file: f, line: a + 1u, col: pos - lookup(f.lines[a])}
68 69
      }
      none {
70
        { file: empty_filemap(map), line: 0u, col: 0u }
71 72
      }
    }
73 74
}

M
Marijn Haverbeke 已提交
75 76
fn lookup_char_pos(map: codemap, pos: uint) -> loc {
    fn lookup(pos: file_pos) -> uint { ret pos.ch; }
77 78 79
    ret lookup_pos(map, pos, lookup);
}

M
Marijn Haverbeke 已提交
80 81
fn lookup_byte_pos(map: codemap, pos: uint) -> loc {
    fn lookup(pos: file_pos) -> uint { ret pos.byte; }
82
    ret lookup_pos(map, pos, lookup);
83 84
}

P
Patrick Walton 已提交
85
enum opt_span {
B
Brian Anderson 已提交
86

T
Tim Chevalier 已提交
87
    //hack (as opposed to option), to make `span` compile
P
Patrick Walton 已提交
88 89
    os_none,
    os_some(@span),
90 91
}
type span = {lo: uint, hi: uint, expanded_from: opt_span};
92

93
fn span_to_str(sp: span, cm: codemap) -> str {
94
    let cur = sp;
B
Brian Anderson 已提交
95
    let res = "";
96
    // FIXME: Should probably be doing pointer comparison on filemap
97
    let prev_file = none;
B
Brian Anderson 已提交
98
    while true {
99 100
        let lo = lookup_char_pos(cm, cur.lo);
        let hi = lookup_char_pos(cm, cur.hi);
101
        res +=
102
            #fmt["%s:%u:%u: %u:%u",
103
                 if some(lo.file.name) == prev_file {
B
Brian Anderson 已提交
104
                     "-"
105
                 } else { lo.file.name }, lo.line, lo.col, hi.line, hi.col];
106
        alt cur.expanded_from {
107
          os_none { break; }
108 109
          os_some(new_sp) {
            cur = *new_sp;
110
            prev_file = some(lo.file.name);
B
Brian Anderson 已提交
111
            res += "<<";
112 113 114 115 116
          }
        }
    }

    ret res;
117 118
}

119
type file_lines = {file: filemap, lines: [uint]};
120

M
Marijn Haverbeke 已提交
121 122 123
fn span_to_lines(sp: span, cm: codemap::codemap) -> @file_lines {
    let lo = lookup_char_pos(cm, sp.lo);
    let hi = lookup_char_pos(cm, sp.hi);
124
    // FIXME: Check for filemap?
B
Brian Anderson 已提交
125
    let lines = [];
126
    uint::range(lo.line - 1u, hi.line as uint) {|i| lines += [i]; };
127
    ret @{file: lo.file, lines: lines};
128 129
}

K
Kevin Cantu 已提交
130
fn get_line(fm: filemap, line: int) -> str unsafe {
B
Brian Anderson 已提交
131
    let begin: uint = fm.lines[line].byte - fm.start_pos.byte;
M
Marijn Haverbeke 已提交
132
    let end: uint;
B
Brian Anderson 已提交
133
    if line as uint < vec::len(fm.lines) - 1u {
B
Brian Anderson 已提交
134
        end = fm.lines[line + 1].byte - fm.start_pos.byte;
135 136 137 138
    } else {
        // If we're not done parsing the file, we're at the limit of what's
        // parsed. If we just slice the rest of the string, we'll print out
        // the remainder of the file, which is undesirable.
139
        end = str::byte_len(*fm.src);
140
        let rest = str::unsafe::slice_bytes(*fm.src, begin, end);
141
        let newline = str::index(rest, '\n' as u8);
M
Marijn Haverbeke 已提交
142
        if newline != -1 { end = begin + (newline as uint); }
143
    }
144
    ret str::unsafe::slice_bytes(*fm.src, begin, end);
145 146
}

147 148 149 150 151
fn lookup_byte_offset(cm: codemap::codemap, chpos: uint)
    -> {fm: filemap, pos: uint}
{
    fn lookup(pos: file_pos) -> uint { ret pos.ch; }
    let {fm,line} = option::get(lookup_line(cm,chpos,lookup));
K
Kevin Atkinson 已提交
152
    let line_offset = fm.lines[line].byte - fm.start_pos.byte;
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
    let col = chpos - fm.lines[line].ch;
    let col_offset = str::byte_len_range(*fm.src, line_offset, col);
    ret {fm: fm, pos: line_offset + col_offset};
}

fn span_to_snippet(sp: span, cm: codemap::codemap) -> str {
    let begin = lookup_byte_offset(cm,sp.lo);
    let end   = lookup_byte_offset(cm,sp.hi);
    assert begin.fm == end.fm;
    ret str::slice(*begin.fm.src, begin.pos, end.pos);
}

fn get_snippet(cm: codemap::codemap, fidx: uint, lo: uint, hi: uint) -> str
{
    let fm = cm.files[fidx];
    ret str::slice(*fm.src, lo, hi)
}

B
Brian Anderson 已提交
171 172
fn get_filemap(cm: codemap, filename: str) -> filemap {
    for fm: filemap in cm.files { if fm.name == filename { ret fm; } }
173 174
    //XXjdm the following triggers a mismatched type bug
    //      (or expected function, found _|_)
M
Marijn Haverbeke 已提交
175
    fail; // ("asking for " + filename + " which we don't know about");
176
}
177

178 179 180 181 182 183 184 185 186
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//