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
146
147
148
149
150
use std::path::PathBuf;
use hemtt_tokens::{Symbol, Token};
use serde::Serialize;
/// Output of preprocessing a file
pub struct Processed {
sources: Vec<(String, String)>,
mappings: Vec<Vec<Mapping>>,
output: String,
}
impl Processed {
#[must_use]
/// Get the output of preprocessing a file
pub fn output(&self) -> &str {
&self.output
}
#[must_use]
/// Get the source map for the processed file
/// Work in progress, does not produce a valid source map yet
///
/// # Panics
/// Panics if the processed file is not in the same directory as the source file
pub fn get_source_map(&self, processed: PathBuf) -> String {
#[derive(Serialize)]
struct Intermediate {
version: u8,
file: PathBuf,
sources: Vec<String>,
names: Vec<()>,
mappings: Vec<Vec<(usize, usize, usize, usize)>>,
}
serde_json::to_string(&Intermediate {
version: 3,
names: Vec::new(),
file: processed,
sources: self.sources.iter().map(|(path, _)| path.clone()).collect(),
mappings: {
self.mappings
.iter()
.map(|o| {
o.iter()
.map(|i| {
(
i.processed_column,
i.source,
i.original_line,
i.original_column,
)
})
.collect::<Vec<(usize, usize, usize, usize)>>()
})
.collect::<Vec<Vec<(usize, usize, usize, usize)>>>()
},
})
.unwrap()
}
}
#[allow(clippy::fallible_impl_from)] // TODO
impl From<Vec<Token>> for Processed {
fn from(tokens: Vec<Token>) -> Self {
let mut sources: Vec<(String, String)> = Vec::new();
let mut mappings = Vec::new();
let mut output = String::new();
let mut mapping = Vec::new();
let mut next_offset = 0;
for token in tokens {
let source = token.source();
let symbol = token.symbol();
let render = symbol.output();
if render.is_empty() {
continue;
}
let original_line = source.start().1 .0;
let original_column = source.start().1 .1;
let source = sources
.iter()
.position(|(name, _)| name == &source.path().to_string())
.map_or_else(
|| {
sources.push((source.path().to_string(), {
if source.path() == "%builtin%" {
String::new()
} else {
std::fs::read_to_string(source.path()).unwrap()
}
}));
sources.len() - 1
},
|index| index,
);
if symbol == &Symbol::Newline {
mappings.push(mapping);
mapping = Vec::new();
next_offset = 0;
} else {
mapping.push(Mapping {
processed_column: next_offset,
source,
original_line,
original_column,
});
next_offset = render.len();
}
output.push_str(render.as_str());
}
Self {
sources,
mappings,
output,
}
}
}
/// Mapping of a processed token to its source
pub struct Mapping {
processed_column: usize,
source: usize,
original_line: usize,
original_column: usize,
}
impl Mapping {
#[must_use]
/// Get the column of the processed token
pub const fn processed_column(&self) -> usize {
self.processed_column
}
#[must_use]
/// Get the source of the processed token
pub const fn source(&self) -> usize {
self.source
}
#[must_use]
/// Get the line of the original token
pub const fn original_line(&self) -> usize {
self.original_line
}
#[must_use]
/// Get the column of the original token
pub const fn original_column(&self) -> usize {
self.original_column
}
}