mirror of
https://github.com/denoland/deno.git
synced 2025-09-27 12:49:10 +00:00
fix(std/encoding/toml): Comment after arrays causing incorrect output (#7224)
This commit is contained in:
parent
935c92800f
commit
03a3256e9c
4 changed files with 128 additions and 32 deletions
4
std/encoding/testdata/arrays.toml
vendored
4
std/encoding/testdata/arrays.toml
vendored
|
@ -1,8 +1,8 @@
|
||||||
[arrays]
|
[arrays]
|
||||||
data = [ ["gamma", "delta"], [1, 2] ]
|
data = [ ["gamma", "delta"], [1, 2] ] # comment after an array caused issue #7072
|
||||||
|
|
||||||
# Line breaks are OK when inside arrays
|
# Line breaks are OK when inside arrays
|
||||||
hosts = [
|
hosts = [
|
||||||
"alpha",
|
"alpha",
|
||||||
"omega"
|
"omega"
|
||||||
]
|
] # comment
|
29
std/encoding/testdata/comment.toml
vendored
Normal file
29
std/encoding/testdata/comment.toml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# This is a full-line comment
|
||||||
|
str0 = 'value' # This is a comment at the end of a line
|
||||||
|
str1 = "# This is not a comment" # but this is
|
||||||
|
str2 = """ # this is not a comment!
|
||||||
|
A multiline string with a #
|
||||||
|
# this is also not a comment
|
||||||
|
""" # this is definitely a comment
|
||||||
|
|
||||||
|
str3 = '''
|
||||||
|
"# not a comment"
|
||||||
|
# this is a real tab on purpose
|
||||||
|
# not a comment
|
||||||
|
''' # comment
|
||||||
|
|
||||||
|
point0 = { x = 1, y = 2, str0 = "#not a comment", z = 3 } # comment
|
||||||
|
point1 = { x = 7, y = 8, z = 9, str0 = "#not a comment"} # comment
|
||||||
|
|
||||||
|
[deno] # this comment is fine
|
||||||
|
features = ["#secure by default", "supports typescript # not a comment"] # Comment caused Issue #7072
|
||||||
|
url = "https://deno.land/" # comment
|
||||||
|
is_not_node = true # comment
|
||||||
|
|
||||||
|
[toml] # Comment caused Issue #7072 (case 2)
|
||||||
|
name = "Tom's Obvious, Minimal Language"
|
||||||
|
objectives = [ # Comment
|
||||||
|
"easy to read", # Comment
|
||||||
|
"minimal config file",
|
||||||
|
"#not a comment" # comment
|
||||||
|
] # comment
|
|
@ -32,14 +32,82 @@ class Parser {
|
||||||
for (let i = 0; i < this.tomlLines.length; i++) {
|
for (let i = 0; i < this.tomlLines.length; i++) {
|
||||||
const s = this.tomlLines[i];
|
const s = this.tomlLines[i];
|
||||||
const trimmed = s.trim();
|
const trimmed = s.trim();
|
||||||
if (trimmed !== "" && trimmed[0] !== "#") {
|
if (trimmed !== "") {
|
||||||
out.push(s);
|
out.push(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.tomlLines = out;
|
this.tomlLines = out;
|
||||||
|
this._removeComments();
|
||||||
this._mergeMultilines();
|
this._mergeMultilines();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_removeComments(): void {
|
||||||
|
function isFullLineComment(line: string) {
|
||||||
|
return line.match(/^#/) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringStart(line: string) {
|
||||||
|
const m = line.match(/(?:=\s*\[?\s*)("""|'''|"|')/);
|
||||||
|
if (!m) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to know which syntax was used to open the string
|
||||||
|
openStringSyntax = m[1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringEnd(line: string) {
|
||||||
|
// match the syntax used to open the string when searching for string close
|
||||||
|
// e.g. if we open with ''' we must close with a '''
|
||||||
|
const reg = RegExp(`(?<!(=\\s*))${openStringSyntax}(?!(.*"))`);
|
||||||
|
if (!line.match(reg)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
openStringSyntax = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleaned = [];
|
||||||
|
let isOpenString = false;
|
||||||
|
let openStringSyntax = "";
|
||||||
|
for (let i = 0; i < this.tomlLines.length; i++) {
|
||||||
|
const line = this.tomlLines[i];
|
||||||
|
|
||||||
|
// stringStart and stringEnd are separate conditions to
|
||||||
|
// support both single-line and multi-line strings
|
||||||
|
if (!isOpenString && stringStart(line)) {
|
||||||
|
isOpenString = true;
|
||||||
|
}
|
||||||
|
if (isOpenString && stringEnd(line)) {
|
||||||
|
isOpenString = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOpenString && !isFullLineComment(line)) {
|
||||||
|
const out = line.split(
|
||||||
|
/(?<=([\,\[\]\{\}]|".*"|'.*'|\w(?!.*("|')+))\s*)#/gi,
|
||||||
|
);
|
||||||
|
cleaned.push(out[0].trim());
|
||||||
|
} else if (isOpenString || !isFullLineComment(line)) {
|
||||||
|
cleaned.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a single line comment doesnt end on the same line, throw error
|
||||||
|
if (
|
||||||
|
isOpenString && (openStringSyntax === "'" || openStringSyntax === '"')
|
||||||
|
) {
|
||||||
|
throw new TOMLError(`Single-line string is not closed:\n${line}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOpenString) {
|
||||||
|
throw new TOMLError(`Incomplete string until EOF`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tomlLines = cleaned;
|
||||||
|
}
|
||||||
|
|
||||||
_mergeMultilines(): void {
|
_mergeMultilines(): void {
|
||||||
function arrayStart(line: string): boolean {
|
function arrayStart(line: string): boolean {
|
||||||
const reg = /.*=\s*\[/g;
|
const reg = /.*=\s*\[/g;
|
||||||
|
@ -236,7 +304,7 @@ class Parser {
|
||||||
if (invalidArr) {
|
if (invalidArr) {
|
||||||
dataString = dataString.replace(/,]/g, "]");
|
dataString = dataString.replace(/,]/g, "]");
|
||||||
}
|
}
|
||||||
dataString = this._stripComment(dataString);
|
|
||||||
if (dataString[0] === "{" && dataString[dataString.length - 1] === "}") {
|
if (dataString[0] === "{" && dataString[dataString.length - 1] === "}") {
|
||||||
const reg = /([a-zA-Z0-9-_\.]*) (=)/gi;
|
const reg = /([a-zA-Z0-9-_\.]*) (=)/gi;
|
||||||
let result;
|
let result;
|
||||||
|
@ -263,34 +331,6 @@ class Parser {
|
||||||
}
|
}
|
||||||
return eval(dataString);
|
return eval(dataString);
|
||||||
}
|
}
|
||||||
private _stripComment(dataString: string): string {
|
|
||||||
const isString = dataString.startsWith('"') || dataString.startsWith("'");
|
|
||||||
if (isString) {
|
|
||||||
const [quote] = dataString;
|
|
||||||
let indexOfNextQuote = 0;
|
|
||||||
while (
|
|
||||||
(indexOfNextQuote = dataString.indexOf(quote, indexOfNextQuote + 1)) !==
|
|
||||||
-1
|
|
||||||
) {
|
|
||||||
const isEscaped = dataString[indexOfNextQuote - 1] === "\\";
|
|
||||||
if (!isEscaped) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (indexOfNextQuote === -1) {
|
|
||||||
throw new TOMLError("imcomplete string literal");
|
|
||||||
}
|
|
||||||
const endOfString = indexOfNextQuote + 1;
|
|
||||||
return dataString.slice(0, endOfString);
|
|
||||||
}
|
|
||||||
|
|
||||||
const m = /(?:|\[|{).*(?:|\]|})\s*^((?!#).)*/g.exec(dataString);
|
|
||||||
if (m) {
|
|
||||||
return m[0].trim();
|
|
||||||
} else {
|
|
||||||
return dataString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_isLocalTime(str: string): boolean {
|
_isLocalTime(str: string): boolean {
|
||||||
const reg = /(\d{2}):(\d{2}):(\d{2})/;
|
const reg = /(\d{2}):(\d{2}):(\d{2})/;
|
||||||
return reg.test(str);
|
return reg.test(str);
|
||||||
|
|
|
@ -413,3 +413,30 @@ the = "array"
|
||||||
assertEquals(actual, expected);
|
assertEquals(actual, expected);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[TOML] Comments",
|
||||||
|
fn: () => {
|
||||||
|
const expected = {
|
||||||
|
str0: "value",
|
||||||
|
str1: "# This is not a comment",
|
||||||
|
str2:
|
||||||
|
" # this is not a comment!\nA multiline string with a #\n# this is also not a comment",
|
||||||
|
str3:
|
||||||
|
'"# not a comment"\n\t# this is a real tab on purpose \n# not a comment',
|
||||||
|
point0: { x: 1, y: 2, str0: "#not a comment", z: 3 },
|
||||||
|
point1: { x: 7, y: 8, z: 9, str0: "#not a comment" },
|
||||||
|
deno: {
|
||||||
|
features: ["#secure by default", "supports typescript # not a comment"],
|
||||||
|
url: "https://deno.land/",
|
||||||
|
is_not_node: true,
|
||||||
|
},
|
||||||
|
toml: {
|
||||||
|
name: "Tom's Obvious, Minimal Language",
|
||||||
|
objectives: ["easy to read", "minimal config file", "#not a comment"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const actual = parseFile(path.join(testFilesDir, "comment.toml"));
|
||||||
|
assertEquals(actual, expected);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue