js: fix codegen for multiline strings (#6791)

pull/6879/head
pancake 2020-11-18 18:23:44 +01:00 committed by GitHub
parent 6d99f0af50
commit d8f64f516b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 104 additions and 100 deletions

View File

@ -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'}`

View File

@ -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

View File

@ -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()
}