From 60fbcc37fcfd09355f4eb7f9cf2861f878b181f8 Mon Sep 17 00:00:00 2001 From: Marcin Date: Thu, 24 Sep 2020 18:18:10 +0200 Subject: [PATCH] gen: scape string function in gen/str.v (#6452) --- vlib/builtin/string_test.v | 2 +- vlib/v/gen/str.v | 100 +++++++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/vlib/builtin/string_test.v b/vlib/builtin/string_test.v index 2eb58a4926..1785810871 100644 --- a/vlib/builtin/string_test.v +++ b/vlib/builtin/string_test.v @@ -619,7 +619,7 @@ fn test_for_loop_two() { fn test_quote() { a := `'` - println("testing double quotes") + println("testing double quotes \"") b := "hi" assert b == 'hi' assert a.str() == '\'' diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index 6e77870727..3751f8e292 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -5,6 +5,97 @@ module gen import v.ast import v.table +fn smart_quote(str string, raw bool) string { + len := str.len + if len == 0 { + return str + } + mut result := '' + mut pos := -1 + mut last := '' + 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 raw && current == '\\' { + toadd = '\\\\' + } + // keep newlines in string + if current == '\n' { + toadd = '\\n' + current = '' + } + if current == '\r' && next == '\n' { + toadd = '\r\n' + current = '' + skip_next = true + } + // backslash + if !raw && current == '\\' { + // escaped backslash - keep as is + if next == '\\' { + toadd = '\\\\' + skip_next = true + } + // keep raw escape squence + else { + if next != '' { + if raw { + toadd = '\\\\' + next + skip_next = true + } + // escape it + else { + toadd = '\\' + next + skip_next = true + } + } + } + } + // Dolar sign + if !raw && current == '$' { + if last == '\\' { + toadd = '\\$' + } + } + // Windows style new line \r\n + if !raw && current == '\r' { + if next == '\n' { + skip_next = true + toadd = '\\n' + } + } + result = result + toadd + last = current + } + return result +} + fn (mut g Gen) write_str_fn_definitions() { // _STR function can't be defined in vlib g.writeln(" @@ -118,11 +209,11 @@ string _STR_TMP(const char *fmt, ...) { fn (mut g Gen) string_literal(node ast.StringLiteral) { if node.is_raw { - escaped_val := node.val.replace_each(['"', '\\"', '\\', '\\\\']) + escaped_val := smart_quote(node.val, true) g.write('tos_lit("$escaped_val")') return } - escaped_val := node.val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n']) + escaped_val := 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");` @@ -149,7 +240,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 := val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n', '%', '%%']) + escaped_val := smart_quote(val, false) // if val == '' { // break // continue @@ -220,7 +311,8 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { // Build the string with % mut end_string := false for i, val in node.vals { - escaped_val := val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n', '%', '%%']) + mut escaped_val := val.replace_each(['%', '%%']) + escaped_val = smart_quote(escaped_val, false) if i >= node.exprs.len { if escaped_val.len > 0 { end_string = true