mirror of
https://github.com/oven-sh/bun
synced 2026-02-17 22:32:06 +00:00
fix '\' handling, still working on ast of "${""}" and ${""} and then have to fix execution of ["echo", "", ""]
This commit is contained in:
@@ -2711,14 +2711,11 @@ pub fn NewLexer(comptime encoding: StringEncoding) type {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Treat newline preceded by backslash as whitespace
|
||||
// Ignore newline preceeded by backslash
|
||||
else if (char == '\n') {
|
||||
if (comptime bun.Environment.allow_assert) {
|
||||
assert(input.escaped);
|
||||
}
|
||||
if (self.chars.state != .Double) {
|
||||
try self.break_word_impl(true, true, false);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2771,7 +2768,7 @@ pub fn NewLexer(comptime encoding: StringEncoding) type {
|
||||
}
|
||||
|
||||
inline fn isImmediatelyEscapedQuote(self: *@This()) bool {
|
||||
return (self.chars.state == .Double and
|
||||
return ((self.chars.state == .Double or self.chars.state == .Single) and
|
||||
(self.chars.current != null and !self.chars.current.?.escaped and self.chars.current.?.char == '"') and
|
||||
(self.chars.prev != null and !self.chars.prev.?.escaped and self.chars.prev.?.char == '"'));
|
||||
}
|
||||
@@ -3516,31 +3513,37 @@ pub fn ShellCharIter(comptime encoding: StringEncoding) type {
|
||||
}
|
||||
|
||||
pub fn read_char(self: *@This()) ?InputChar {
|
||||
const indexed_value = self.src.index() orelse return null;
|
||||
var char = indexed_value.char;
|
||||
if (char != '\\' or self.state == .Single) return .{ .char = char };
|
||||
while (true) {
|
||||
const indexed_value = self.src.index() orelse return null;
|
||||
var char = indexed_value.char;
|
||||
if (char != '\\' or self.state == .Single) return .{ .char = char };
|
||||
|
||||
// Handle backslash
|
||||
switch (self.state) {
|
||||
.Normal => {
|
||||
const peeked = self.src.indexNext() orelse return null;
|
||||
char = peeked.char;
|
||||
},
|
||||
.Double => {
|
||||
const peeked = self.src.indexNext() orelse return null;
|
||||
switch (peeked.char) {
|
||||
// Backslash only applies to these characters
|
||||
'$', '`', '"', '\\', '\n', '#' => {
|
||||
char = peeked.char;
|
||||
},
|
||||
else => return .{ .char = char, .escaped = false },
|
||||
}
|
||||
},
|
||||
// We checked `self.state == .Single` above so this is impossible
|
||||
.Single => unreachable,
|
||||
// Handle backslash
|
||||
const peeked = self.src.indexNext() orelse return null;
|
||||
if (peeked.char == '\n') {
|
||||
// completely ignore backslash newline, don't advance self.prev/self.current
|
||||
self.src.eat(true);
|
||||
continue;
|
||||
}
|
||||
switch (self.state) {
|
||||
.Normal => {
|
||||
char = peeked.char;
|
||||
},
|
||||
.Double => {
|
||||
switch (peeked.char) {
|
||||
// Backslash only applies to these characters
|
||||
'$', '`', '"', '\\', '#' => {
|
||||
char = peeked.char;
|
||||
},
|
||||
else => return .{ .char = char, .escaped = false },
|
||||
}
|
||||
},
|
||||
// We checked `self.state == .Single` above so this is impossible
|
||||
.Single => unreachable,
|
||||
}
|
||||
|
||||
return .{ .char = char, .escaped = true };
|
||||
}
|
||||
|
||||
return .{ .char = char, .escaped = true };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,11 +15,20 @@ describe("bun shell", () => {
|
||||
expect(await $({ raw: ["echo " + 'a"a"'.repeat(1000000)] } as any).text()).toBe("aa".repeat(1000000) + "\n");
|
||||
});
|
||||
it("passes correct number of arguments with empty string substitutions", async () => {
|
||||
expect(await $`echo ${"1"} ${""} ${"2"}`.text()).toBe("1 2\n");
|
||||
expect(await $`echo 1 ${""} 2`.text()).toBe("1 2\n");
|
||||
});
|
||||
it("passes correct number of arguments with empty string quotes", async () => {
|
||||
it("passes correct number of arguments with empty string substitutions 2", async () => {
|
||||
expect(await $`echo 1 "${""}" 2`.text()).toBe("1 2\n");
|
||||
});
|
||||
it("passes correct number of arguments with empty string substitutions 3", async () => {
|
||||
expect(await $`echo 1 '${""}' 2`.text()).toBe("1 2\n");
|
||||
});
|
||||
it("passes correct number of arguments with empty double string quotes", async () => {
|
||||
expect(await $`echo "1" "" "2"`.text()).toBe("1 2\n");
|
||||
});
|
||||
it("passes correct number of arguments with empty single string quotes", async () => {
|
||||
expect(await $`echo '1' '' '2'`.text()).toBe("1 2\n");
|
||||
});
|
||||
it("doesn't cause invalid js string ref error with a number after a string ref", async () => {
|
||||
expect(await $`echo ${'"'}1`.text()).toBe('"1\n');
|
||||
});
|
||||
@@ -72,7 +81,7 @@ describe("bun shell", () => {
|
||||
it("expands tilde as middle argument", async () => {
|
||||
expect(await $`echo a ~ b`.text()).toBe("a " + process.env.HOME + " b\n");
|
||||
});
|
||||
it.todo("expands tilde as middle argument 2", async () => {
|
||||
it("expands tilde as middle argument 2", async () => {
|
||||
expect(
|
||||
await $`echo a ~\
|
||||
b`.text(),
|
||||
@@ -118,6 +127,24 @@ describe("bun shell", () => {
|
||||
it("does not expand tilde second", async () => {
|
||||
expect(await $`echo "a"~`.text()).toBe("a~\n");
|
||||
});
|
||||
it("handles backslashed newline", async () => {
|
||||
expect(
|
||||
await $`echo a\
|
||||
b`.text(),
|
||||
).toBe("ab\n");
|
||||
});
|
||||
it("handles backslashed newline in single quotes", async () => {
|
||||
expect(
|
||||
await $`echo 'a\
|
||||
b'`.text(),
|
||||
).toBe("a\\\nb\n");
|
||||
});
|
||||
it("handles backslashed newline in double quotes", async () => {
|
||||
expect(
|
||||
await $`echo "a\
|
||||
b"`.text(),
|
||||
).toBe("ab\n");
|
||||
});
|
||||
// TODO: handle username (`~user` -> getpwnam(user) eg /home/user if the accont exists. but only if all unquoted, ie `~user"a"` <- not allowed)
|
||||
|
||||
it("fails for bad surrogate pairs", async () => {
|
||||
|
||||
Reference in New Issue
Block a user