From d8f64f516b2dde32a8816abd54b1936db9155bae Mon Sep 17 00:00:00 2001 From: pancake Date: Wed, 18 Nov 2020 18:23:44 +0100 Subject: [PATCH] js: fix codegen for multiline strings (#6791) --- vlib/v/gen/js/js.v | 3 +- vlib/v/gen/str.v | 104 +++----------------------------------------- vlib/v/util/quote.v | 97 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 100 deletions(-) create mode 100644 vlib/v/util/quote.v diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 1d6f765cd3..cfb5bd7978 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -642,7 +642,8 @@ fn (mut g JsGen) expr(node ast.Expr) { g.gen_string_inter_literal(node) } ast.StringLiteral { - g.write('"$node.val"') + text := node.val.replace('`', '\\`') + g.write('`$text`') } ast.StructInit { // `user := User{name: 'Bob'}` diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index b72185bbfd..8036b5682d 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -3,102 +3,8 @@ module gen import v.ast +import v.util import v.table -import strings - -const ( - invalid_escapes = ['(', '{', '$', '`', '.'] -) - -fn smart_quote(str string, raw bool) string { - len := str.len - if len == 0 { - return str - } - mut result := strings.new_builder(0) - mut pos := -1 - mut last := '' - // TODO: This should be a single char? - mut next := '' - mut skip_next := false - for { - pos = pos + 1 - if skip_next { - skip_next = false - pos = pos + 1 - } - if pos >= len { - break - } - if pos + 1 < len { - unsafe { - next = str.str[pos + 1].str() - } - } - mut current := str - mut toadd := str - if len > 1 { - unsafe { - current = str.str[pos].str() - } - toadd = current - } - // double quote - if current == '"' { - toadd = '\\"' - current = '' - } - if current == '\\' { - if raw { - toadd = '\\\\' - } else { - // escaped backslash - keep as is - if next == '\\' { - toadd = '\\\\' - skip_next = true - } else if next != '' { - if raw { - toadd = '\\\\' + next - skip_next = true - } - // keep all valid escape sequences - else if next !in invalid_escapes { - toadd = '\\' + next - skip_next = true - } else { - toadd = next - skip_next = true - } - } - } - } - // keep newlines in string - if current == '\n' { - toadd = '\\n' - current = '' - } else if current == '\r' && next == '\n' { - toadd = '\r\n' - current = '' - skip_next = true - } - // Dolar sign - if !raw && current == '$' { - if last == '\\' { - toadd = r'\$' - } - } - // Windows style new line \r\n - if !raw && current == '\r' { - if next == '\n' { - skip_next = true - toadd = '\\n' - } - } - result.write(toadd) - last = current - } - return result.str() -} fn (mut g Gen) write_str_fn_definitions() { // _STR function can't be defined in vlib @@ -213,11 +119,11 @@ string _STR_TMP(const char *fmt, ...) { fn (mut g Gen) string_literal(node ast.StringLiteral) { if node.is_raw { - escaped_val := smart_quote(node.val, true) + escaped_val := util.smart_quote(node.val, true) g.write('tos_lit("$escaped_val")') return } - escaped_val := smart_quote(node.val, false) + escaped_val := util.smart_quote(node.val, false) if g.is_c_call || node.language == .c { // In C calls we have to generate C strings // `C.printf("hi")` => `printf("hi");` @@ -244,7 +150,7 @@ fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) { is_nl := call_expr.name == 'writeln' // println('optimize sb $call_expr.name') for i, val in node.vals { - escaped_val := smart_quote(val, false) + escaped_val := util.smart_quote(val, false) // if val == '' { // break // continue @@ -309,7 +215,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { mut end_string := false for i, val in node.vals { mut escaped_val := val.replace_each(['%', '%%']) - escaped_val = smart_quote(escaped_val, false) + escaped_val = util.smart_quote(escaped_val, false) if i >= node.exprs.len { if escaped_val.len > 0 { end_string = true diff --git a/vlib/v/util/quote.v b/vlib/v/util/quote.v new file mode 100644 index 0000000000..03439a1159 --- /dev/null +++ b/vlib/v/util/quote.v @@ -0,0 +1,97 @@ +module util + +import strings + +const ( + invalid_escapes = ['(', '{', '$', '`', '.'] +) + +pub fn smart_quote(str string, raw bool) string { + len := str.len + if len == 0 { + return str + } + mut result := strings.new_builder(0) + mut pos := -1 + mut last := '' + // TODO: This should be a single char? + mut next := '' + mut skip_next := false + for { + pos = pos + 1 + if skip_next { + skip_next = false + pos = pos + 1 + } + if pos >= len { + break + } + if pos + 1 < len { + unsafe { + next = str.str[pos + 1].str() + } + } + mut current := str + mut toadd := str + if len > 1 { + unsafe { + current = str.str[pos].str() + } + toadd = current + } + // double quote + if current == '"' { + toadd = '\\"' + current = '' + } + if current == '\\' { + if raw { + toadd = '\\\\' + } else { + // escaped backslash - keep as is + if next == '\\' { + toadd = '\\\\' + skip_next = true + } else if next != '' { + if raw { + toadd = '\\\\' + next + skip_next = true + } + // keep all valid escape sequences + else if next !in invalid_escapes { + toadd = '\\' + next + skip_next = true + } else { + toadd = next + skip_next = true + } + } + } + } + // keep newlines in string + if current == '\n' { + toadd = '\\n' + current = '' + } else if current == '\r' && next == '\n' { + toadd = '\r\n' + current = '' + skip_next = true + } + // Dolar sign + if !raw && current == '$' { + if last == '\\' { + toadd = r'\$' + } + } + // Windows style new line \r\n + if !raw && current == '\r' { + if next == '\n' { + skip_next = true + toadd = '\\n' + } + } + result.write(toadd) + last = current + } + return result.str() +}