js: fix codegen for multiline strings (#6791)
parent
6d99f0af50
commit
d8f64f516b
|
@ -642,7 +642,8 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
||||||
g.gen_string_inter_literal(node)
|
g.gen_string_inter_literal(node)
|
||||||
}
|
}
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
g.write('"$node.val"')
|
text := node.val.replace('`', '\\`')
|
||||||
|
g.write('`$text`')
|
||||||
}
|
}
|
||||||
ast.StructInit {
|
ast.StructInit {
|
||||||
// `user := User{name: 'Bob'}`
|
// `user := User{name: 'Bob'}`
|
||||||
|
|
104
vlib/v/gen/str.v
104
vlib/v/gen/str.v
|
@ -3,102 +3,8 @@
|
||||||
module gen
|
module gen
|
||||||
|
|
||||||
import v.ast
|
import v.ast
|
||||||
|
import v.util
|
||||||
import v.table
|
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() {
|
fn (mut g Gen) write_str_fn_definitions() {
|
||||||
// _STR function can't be defined in vlib
|
// _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) {
|
fn (mut g Gen) string_literal(node ast.StringLiteral) {
|
||||||
if node.is_raw {
|
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")')
|
g.write('tos_lit("$escaped_val")')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
escaped_val := smart_quote(node.val, false)
|
escaped_val := util.smart_quote(node.val, false)
|
||||||
if g.is_c_call || node.language == .c {
|
if g.is_c_call || node.language == .c {
|
||||||
// In C calls we have to generate C strings
|
// In C calls we have to generate C strings
|
||||||
// `C.printf("hi")` => `printf("hi");`
|
// `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'
|
is_nl := call_expr.name == 'writeln'
|
||||||
// println('optimize sb $call_expr.name')
|
// println('optimize sb $call_expr.name')
|
||||||
for i, val in node.vals {
|
for i, val in node.vals {
|
||||||
escaped_val := smart_quote(val, false)
|
escaped_val := util.smart_quote(val, false)
|
||||||
// if val == '' {
|
// if val == '' {
|
||||||
// break
|
// break
|
||||||
// continue
|
// continue
|
||||||
|
@ -309,7 +215,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||||
mut end_string := false
|
mut end_string := false
|
||||||
for i, val in node.vals {
|
for i, val in node.vals {
|
||||||
mut escaped_val := val.replace_each(['%', '%%'])
|
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 i >= node.exprs.len {
|
||||||
if escaped_val.len > 0 {
|
if escaped_val.len > 0 {
|
||||||
end_string = true
|
end_string = true
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
Loading…
Reference in New Issue