buf must be at least as long as source
In-place transformation is supported (i.e. source and buf can be the same slice)
pub fn removeComments(source: []const u8, buf: []u8, source_mappings: ?*SourceMappings) ![]u8
pub fn removeComments(source: []const u8, buf: []u8, source_mappings: ?*SourceMappings) ![]u8 {
std.debug.assert(buf.len >= source.len);
var result = UncheckedSliceWriter{ .slice = buf };
const State = enum {
start,
forward_slash,
line_comment,
multiline_comment,
multiline_comment_end,
single_quoted,
single_quoted_escape,
double_quoted,
double_quoted_escape,
};
var state: State = .start;
var index: usize = 0;
var pending_start: ?usize = null;
var line_handler = LineHandler{ .buffer = source };
while (index < source.len) : (index += 1) {
const c = source[index];
// TODO: Disallow \x1A, \x00, \x7F in comments. At least \x1A and \x00 can definitely
// cause errors or parsing weirdness in the Win32 RC compiler. These are disallowed
// in the lexer, but comments are stripped before getting to the lexer.
switch (state) {
.start => switch (c) {
'/' => {
state = .forward_slash;
pending_start = index;
},
'\r', '\n' => {
_ = line_handler.incrementLineNumber(index);
result.write(c);
},
else => {
switch (c) {
'"' => state = .double_quoted,
'\'' => state = .single_quoted,
else => {},
}
result.write(c);
},
},
.forward_slash => switch (c) {
'/' => state = .line_comment,
'*' => {
state = .multiline_comment;
},
else => {
_ = line_handler.maybeIncrementLineNumber(index);
result.writeSlice(source[pending_start.? .. index + 1]);
pending_start = null;
state = .start;
},
},
.line_comment => switch (c) {
'\r', '\n' => {
_ = line_handler.incrementLineNumber(index);
result.write(c);
state = .start;
},
else => {},
},
.multiline_comment => switch (c) {
'\r' => try handleMultilineCarriageReturn(source, &line_handler, index, &result, source_mappings),
'\n' => {
_ = line_handler.incrementLineNumber(index);
result.write(c);
},
'*' => state = .multiline_comment_end,
else => {},
},
.multiline_comment_end => switch (c) {
'\r' => {
try handleMultilineCarriageReturn(source, &line_handler, index, &result, source_mappings);
// We only want to treat this as a newline if it's part of a CRLF pair. If it's
// not, then we still want to stay in .multiline_comment_end, so that e.g. `*<\r>/` still
// functions as a `*/` comment ending. Kinda crazy, but that's how the Win32 implementation works.
if (formsLineEndingPair(source, '\r', index + 1)) {
state = .multiline_comment;
}
},
'\n' => {
_ = line_handler.incrementLineNumber(index);
result.write(c);
state = .multiline_comment;
},
'/' => {
state = .start;
},
else => {
state = .multiline_comment;
},
},
.single_quoted => switch (c) {
'\r', '\n' => {
_ = line_handler.incrementLineNumber(index);
state = .start;
result.write(c);
},
'\\' => {
state = .single_quoted_escape;
result.write(c);
},
'\'' => {
state = .start;
result.write(c);
},
else => {
result.write(c);
},
},
.single_quoted_escape => switch (c) {
'\r', '\n' => {
_ = line_handler.incrementLineNumber(index);
state = .start;
result.write(c);
},
else => {
state = .single_quoted;
result.write(c);
},
},
.double_quoted => switch (c) {
'\r', '\n' => {
_ = line_handler.incrementLineNumber(index);
state = .start;
result.write(c);
},
'\\' => {
state = .double_quoted_escape;
result.write(c);
},
'"' => {
state = .start;
result.write(c);
},
else => {
result.write(c);
},
},
.double_quoted_escape => switch (c) {
'\r', '\n' => {
_ = line_handler.incrementLineNumber(index);
state = .start;
result.write(c);
},
else => {
state = .double_quoted;
result.write(c);
},
},
}
} else {
switch (state) {
.start,
.line_comment,
.multiline_comment,
.multiline_comment_end,
.single_quoted,
.single_quoted_escape,
.double_quoted,
.double_quoted_escape,
=> {},
.forward_slash => {
result.writeSlice(source[pending_start.?..index]);
},
}
}
return result.getWritten();
}