diff --git a/examples/vweb/vweb_assets/index.html b/examples/vweb/vweb_assets/index.html index 40ad34c103..b3d81320de 100644 --- a/examples/vweb/vweb_assets/index.html +++ b/examples/vweb/vweb_assets/index.html @@ -1,7 +1,7 @@
@title - + @css 'index.css'
diff --git a/vlib/net/html/parser_test.v b/vlib/net/html/parser_test.v index 4b572de290..274a47c2ab 100644 --- a/vlib/net/html/parser_test.v +++ b/vlib/net/html/parser_test.v @@ -34,7 +34,7 @@ fn test_giant_string() { fn test_script_tag() { mut parser := Parser{} - script_content := "\nvar googletag = googletag || {};\ngoogletag.cmd = googletag.cmd || [];if(3 > 5) { console.log('Birl');}\n" + script_content := "\nvar googletag = googletag || {};\ngoogletag.cmd = googletag.cmd || [];if(3 > 5) {console.log('Birl');}\n" temp_html := '' parser.parse_html(temp_html) assert parser.tags[2].content.len == script_content.replace('\n', '').len diff --git a/vlib/v/checker/tests/str_interpol_invalid_err.out b/vlib/v/checker/tests/str_interpol_invalid_err.out index 28f5252eef..99973fcaa7 100644 --- a/vlib/v/checker/tests/str_interpol_invalid_err.out +++ b/vlib/v/checker/tests/str_interpol_invalid_err.out @@ -4,45 +4,31 @@ vlib/v/checker/tests/str_interpol_invalid_err.vv:8:13: error: illegal format spe 8 | _ = '${[1]:x}' | ^ 9 | _ = '${[1]!:x}' - 10 | _ = '${Foo{}:x}' + 10 | // _ = '${Foo{}:x}' vlib/v/checker/tests/str_interpol_invalid_err.vv:9:14: error: illegal format specifier `x` for type `[1]int` 7 | fn main() { 8 | _ = '${[1]:x}' 9 | _ = '${[1]!:x}' | ^ - 10 | _ = '${Foo{}:x}' + 10 | // _ = '${Foo{}:x}' 11 | _ = '${[1]:f}' -vlib/v/checker/tests/str_interpol_invalid_err.vv:10:15: error: illegal format specifier `x` for type `Foo` - 8 | _ = '${[1]:x}' - 9 | _ = '${[1]!:x}' - 10 | _ = '${Foo{}:x}' - | ^ - 11 | _ = '${[1]:f}' - 12 | _ := '${none:F}' vlib/v/checker/tests/str_interpol_invalid_err.vv:11:13: error: illegal format specifier `f` for type `[]int` 9 | _ = '${[1]!:x}' - 10 | _ = '${Foo{}:x}' + 10 | // _ = '${Foo{}:x}' 11 | _ = '${[1]:f}' | ^ 12 | _ := '${none:F}' 13 | // _ = '${{"a": "b"}:x}' vlib/v/checker/tests/str_interpol_invalid_err.vv:12:15: error: illegal format specifier `F` for type `none` - 10 | _ = '${Foo{}:x}' + 10 | // _ = '${Foo{}:x}' 11 | _ = '${[1]:f}' 12 | _ := '${none:F}' | ^ 13 | // _ = '${{"a": "b"}:x}' - 14 | _ = '${Alias(Foo{}):x}' -vlib/v/checker/tests/str_interpol_invalid_err.vv:14:22: error: illegal format specifier `x` for type `Alias` - 12 | _ := '${none:F}' - 13 | // _ = '${{"a": "b"}:x}' - 14 | _ = '${Alias(Foo{}):x}' - | ^ - 15 | _ = '${SumType(int(5)):o}' - 16 | } + 14 | // _ = '${Alias(Foo{}):x}' vlib/v/checker/tests/str_interpol_invalid_err.vv:15:25: error: illegal format specifier `o` for type `SumType` 13 | // _ = '${{"a": "b"}:x}' - 14 | _ = '${Alias(Foo{}):x}' + 14 | // _ = '${Alias(Foo{}):x}' 15 | _ = '${SumType(int(5)):o}' | ^ 16 | } diff --git a/vlib/v/checker/tests/str_interpol_invalid_err.vv b/vlib/v/checker/tests/str_interpol_invalid_err.vv index 556a798f33..7a4c5ab4fc 100644 --- a/vlib/v/checker/tests/str_interpol_invalid_err.vv +++ b/vlib/v/checker/tests/str_interpol_invalid_err.vv @@ -7,10 +7,10 @@ type SumType = Alias | int fn main() { _ = '${[1]:x}' _ = '${[1]!:x}' - _ = '${Foo{}:x}' + // _ = '${Foo{}:x}' _ = '${[1]:f}' _ := '${none:F}' // _ = '${{"a": "b"}:x}' - _ = '${Alias(Foo{}):x}' + // _ = '${Alias(Foo{}):x}' _ = '${SumType(int(5)):o}' } diff --git a/vlib/v/eval/gen/infix_gen.v b/vlib/v/eval/gen/infix_gen.v index ab116573a2..8139b5b3fc 100644 --- a/vlib/v/eval/gen/infix_gen.v +++ b/vlib/v/eval/gen/infix_gen.v @@ -9,7 +9,7 @@ const ( module eval import v.token import v.ast -fn(e Eval)infix_expr(left Object,right Object,op token.Kind,expecting ast.Type)Object{ match op{' +fn(e Eval)infix_expr(left Object,right Object,op token.Kind,expecting ast.Type)Object{match op{' footer = "else{ e.error('unknown infix expression: \$op')}}return empty // should e.error before this anyway } " @@ -37,26 +37,26 @@ fn main() { b.write_string(header) for enm, op in comparison { - b.write_string('.$enm{ match left{') + b.write_string('.$enm{match left{') for ct in compound_types { - b.write_string('$ct { match right{') + b.write_string('$ct {match right{') for ct2 in compound_types { - b.write_string('$ct2{ return left.val${op}right.val}') + b.write_string('$ct2{return left.val${op}right.val}') } for lt2 in literal_types { - b.write_string('$lt2{ return left.val${op}right}') + b.write_string('$lt2{return left.val${op}right}') } b.write_string("else{ e.error('invalid operands to $op: $ct and \$right.type_name()')}}}") } for lt in literal_types { - b.write_string('$lt { match right{') + b.write_string('$lt {match right{') for ct2 in compound_types { - b.write_string('$ct2{ return left${op}right.val}') + b.write_string('$ct2{return left${op}right.val}') } for lt2 in literal_types { - b.write_string('$lt2{ return left${op}right}') + b.write_string('$lt2{return left${op}right}') } - b.write_string("else { e.error('invalid operands to $op: ") + b.write_string("else {e.error('invalid operands to $op: ") b.write_string(if lt == 'i64' { 'int' } else { 'float' }) b.write_string(" literal and \$right.type_name()')}}}") } @@ -66,12 +66,12 @@ fn main() { b.write_string("else { e.error('invalid operands to $op: \$left.type_name() and \$right.type_name()')}}}") } for math, op in math_ops { - b.write_string('.$math{ match left{') + b.write_string('.$math{match left{') for ct in compound_types { if op in ['<<', '>>'] && ct == 'Float' { continue } - b.write_string('$ct { match right{') + b.write_string('$ct {match right{') for ct2 in compound_types { if op in ['<<', '>>'] && ct2 == 'Float' { continue @@ -100,7 +100,7 @@ fn main() { if op in ['<<', '>>'] && lt == 'f64' { continue } - b.write_string('$lt{ match right{') + b.write_string('$lt{match right{') for ct2 in compound_types { if op in ['<<', '>>'] && ct2 == 'Float' { continue @@ -117,13 +117,13 @@ fn main() { continue } unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' } - b.write_string('$lt2{ if expecting in ast.signed_integer_type_idxs{ return Int{ i64(left)${op}i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{ return Uint{ u64(left)${op}u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right))$unsafe_end}') + b.write_string('$lt2{if expecting in ast.signed_integer_type_idxs{ return Int{ i64(left)${op}i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{ return Uint{ u64(left)${op}u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right))$unsafe_end}') if op !in ['<<', '>>'] { b.write_string('else if expecting in ast.float_type_idxs{ return Float{ f64(left)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{ return f64(f64(left)${op}f64(right))}') } b.write_string(uk_expect_footer) } - b.write_string("else { e.error('invalid operands to $op: ") + b.write_string("else {e.error('invalid operands to $op: ") b.write_string(if lt == 'i64' { 'int' } else { 'float' }) b.write_string(" literal and \$right.type_name()')}}}") } diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index 94840d4c62..7ace8ff271 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -803,13 +803,11 @@ fn (mut s Scanner) text_scan() token.Token { } `{` { if s.is_inside_string { - prev_char := s.text[s.pos - 1] - next_char := s.text[s.pos + 1] // Handle new `hello {name}` string interpolation - if !next_char.is_space() && next_char != `}` && prev_char != `$` { + if !s.text[s.pos + 1].is_space() && s.text[s.pos - 1] != `$` { return s.new_token(.str_dollar, '', 1) } - if prev_char == `$` { + if s.text[s.pos - 1] == `$` { // Skip { in `${` in strings continue } else { @@ -1176,7 +1174,6 @@ fn (mut s Scanner) ident_string() string { } c := s.text[s.pos] prevc := s.text[s.pos - 1] - nextc := s.text[s.pos + 1] if c == scanner.backslash { backslash_count++ } @@ -1198,23 +1195,24 @@ fn (mut s Scanner) ident_string() string { if backslash_count % 2 == 1 && !is_raw && !is_cstr { // Escape `\x` if c == `x` { - if nextc == s.quote || !(nextc.is_hex_digit() && s.text[s.pos + 2].is_hex_digit()) { + if s.text[s.pos + 1] == s.quote || !(s.text[s.pos + 1].is_hex_digit() + && s.text[s.pos + 2].is_hex_digit()) { s.error(r'`\x` used without two following hex digits') } h_escapes_pos << s.pos - 1 } // Escape `\u` if c == `u` { - if nextc == s.quote || s.text[s.pos + 2] == s.quote || s.text[s.pos + 3] == s.quote - || s.text[s.pos + 4] == s.quote || !nextc.is_hex_digit() - || !s.text[s.pos + 2].is_hex_digit() || !s.text[s.pos + 3].is_hex_digit() - || !s.text[s.pos + 4].is_hex_digit() { + if s.text[s.pos + 1] == s.quote || s.text[s.pos + 2] == s.quote + || s.text[s.pos + 3] == s.quote || s.text[s.pos + 4] == s.quote + || !s.text[s.pos + 1].is_hex_digit() || !s.text[s.pos + 2].is_hex_digit() + || !s.text[s.pos + 3].is_hex_digit() || !s.text[s.pos + 4].is_hex_digit() { s.error(r'`\u` incomplete unicode character value') } u_escapes_pos << s.pos - 1 } } - // '${var}' (ignore in vfmt mode) (skip \$) + // ${var} (ignore in vfmt mode) (skip \$) if prevc == `$` && c == `{` && !is_raw && s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 { s.is_inside_string = true @@ -1223,7 +1221,7 @@ fn (mut s Scanner) ident_string() string { s.pos -= 2 break } - // '$var' + // $var if prevc == `$` && util.is_name_char(c) && !is_raw && s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 { s.is_inside_string = true @@ -1231,12 +1229,25 @@ fn (mut s Scanner) ident_string() string { s.pos -= 2 break } - // '{var}' (ignore in vfmt mode) (skip \{) - if c == `{` && (util.is_name_char(nextc) || nextc == `@`) && prevc != `$` && !is_raw + // {var} (ignore in vfmt mode) (skip \{) + if c == `{` && util.is_name_char(s.text[s.pos + 1]) && prevc != `$` && !is_raw && s.count_symbol_before(s.pos - 1, scanner.backslash) % 2 == 0 { // Detect certain strings with "{" that are not interpolation: // e.g. "{init: " (no "}" at the end) - if s.is_valid_interpolation(s.pos + 1) { + mut is_valid_inter := true + for i := s.pos + 1; i < s.text.len; i++ { + if s.text[i] == `}` { + break + } + if s.text[i] in [`=`, `:`, `\n`, s.inter_quote] { + // We reached the end of the line or string without reaching "}". + // Also if there's "=", there's no way it's a valid interpolation expression: + // e.g. `println("{a.b = 42}")` `println('{foo:bar}')` + is_valid_inter = false + break + } + } + if is_valid_inter { s.is_inside_string = true s.is_enclosed_inter = true // so that s.pos points to $ at the next step @@ -1244,19 +1255,6 @@ fn (mut s Scanner) ident_string() string { break } } - // '{var1}{var2}' - if prevc == `{` && (util.is_name_char(c) || c == `@`) && s.text[s.pos - 2] !in [`$`, `{`] - && !is_raw && s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 { - // Detect certain strings with "{" that are not interpolation: - // e.g. "{init: " (no "}" at the end) - if s.is_valid_interpolation(s.pos) { - s.is_inside_string = true - s.is_enclosed_inter = true - // so that s.pos points to $ at the next step - s.pos -= 2 - break - } - } if c != scanner.backslash { backslash_count = 0 } @@ -1312,39 +1310,6 @@ fn (mut s Scanner) ident_string() string { return lit } -fn (s Scanner) is_valid_interpolation(start_pos int) bool { - mut is_valid_inter := true - mut has_rcbr := false - mut inside_par := false - for i := start_pos; i < s.text.len - 1; i++ { - if s.text[i] == `(` { - inside_par = true - } - if s.text[i] == `)` && inside_par { - inside_par = false - } - if s.text[i] == s.quote && !inside_par { - break - } - if s.text[i] == `}` { - // No } in this string, so it's not a valid `{x}` interpolation - has_rcbr = true - } - if s.text[i] == `=` && (s.text[i - 1] in [`!`, `>`, `<`] || s.text[i + 1] == `=`) { - // `!=` `>=` `<=` `==` - return true - } - if s.text[i] == `=` || (s.text[i] == `:` && s.text[i + 1].is_space()) { - // We reached the end of the line or string without reaching "}". - // Also if there's "=", there's no way it's a valid interpolation expression: - // e.g. `println("{a.b = 42}")` `println('{foo: bar}')` - is_valid_inter = false - break - } - } - return is_valid_inter && has_rcbr -} - fn decode_h_escape_single(str string, idx int) (int, string) { end_idx := idx + 4 // "\xXX".len == 4 diff --git a/vlib/v/tests/string_interpolation_inner_cbr_test.v b/vlib/v/tests/string_interpolation_inner_cbr_test.v index a1c03df9f4..5dd0df090f 100644 --- a/vlib/v/tests/string_interpolation_inner_cbr_test.v +++ b/vlib/v/tests/string_interpolation_inner_cbr_test.v @@ -9,9 +9,12 @@ fn test_string_interpolation_inner_cbr() { println(s1) assert s1 == '22' + /* + XTODO s2 := '${St{}}' println(s2) assert s2 == 'St{}' + */ s3 := '${{ 'a': 1