mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
jsgen: initial string interpolation support
This commit is contained in:
parent
123d788d0d
commit
6f886dccca
@ -1098,6 +1098,7 @@ fn (mut g JsGen) gen_assign_expr(it ast.AssignExpr) {
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.write('const $tmp_var = ')
|
||||
g.expr(it.val)
|
||||
return
|
||||
}
|
||||
|
||||
// NB: The expr has to go *before* inside_map_set as it's defined there
|
||||
@ -1326,10 +1327,9 @@ fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) {
|
||||
}
|
||||
|
||||
fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
|
||||
// TODO Implement `tos3`
|
||||
g.write('tos3(`')
|
||||
g.write('`')
|
||||
for i, val in it.vals {
|
||||
escaped_val := val.replace_each(['`', '\`', '\r\n', '\n'])
|
||||
escaped_val := val.replace('`', '\\`')
|
||||
g.write(escaped_val)
|
||||
if i >= it.exprs.len {
|
||||
continue
|
||||
@ -1338,39 +1338,19 @@ fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
|
||||
sfmt := it.expr_fmts[i]
|
||||
g.write('\${')
|
||||
if sfmt.len > 0 {
|
||||
fspec := sfmt[sfmt.len - 1]
|
||||
if fspec == `s` && it.expr_types[i] == table.string_type {
|
||||
g.expr(expr)
|
||||
g.write('.str')
|
||||
} else {
|
||||
g.expr(expr)
|
||||
}
|
||||
} else if it.expr_types[i] == table.string_type {
|
||||
// `name.str`
|
||||
// TODO: Handle formatting
|
||||
g.expr(expr)
|
||||
g.write('.str')
|
||||
} else if it.expr_types[i] == table.bool_type {
|
||||
// `expr ? "true" : "false"`
|
||||
g.expr(expr)
|
||||
g.write(' ? "true" : "false"')
|
||||
} else {
|
||||
sym := g.table.get_type_symbol(it.expr_types[i])
|
||||
|
||||
match sym.kind {
|
||||
.struct_ {
|
||||
g.expr(expr)
|
||||
if sym.has_method('str') {
|
||||
g.write('.str()')
|
||||
}
|
||||
}
|
||||
else {
|
||||
g.expr(expr)
|
||||
}
|
||||
g.expr(expr)
|
||||
if sym.kind == .struct_ && sym.has_method('str') {
|
||||
g.write('.str()')
|
||||
}
|
||||
}
|
||||
g.write('}')
|
||||
}
|
||||
g.write('`)')
|
||||
g.write('`')
|
||||
}
|
||||
|
||||
fn (mut g JsGen) gen_struct_init(it ast.StructInit) {
|
||||
|
260
vlib/v/gen/js/tests/interp.js
Normal file
260
vlib/v/gen/js/tests/interp.js
Normal file
@ -0,0 +1,260 @@
|
||||
// V_COMMIT_HASH 8a24d7d
|
||||
// V_CURRENT_COMMIT_HASH 123d788
|
||||
// Generated by the V compiler
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @namespace builtin */
|
||||
const builtin = (function () {
|
||||
/**
|
||||
* @function
|
||||
* @param {any} s
|
||||
* @returns {void}
|
||||
*/
|
||||
function println(s) {
|
||||
console.log(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {any} s
|
||||
* @returns {void}
|
||||
*/
|
||||
function print(s) {
|
||||
process.stdout.write(s);
|
||||
}
|
||||
|
||||
/* module exports */
|
||||
return {
|
||||
println,
|
||||
print
|
||||
};
|
||||
})();
|
||||
|
||||
/** @namespace main */
|
||||
const main = (function () {
|
||||
/**
|
||||
* @function
|
||||
* @param {string} s1
|
||||
* @param {string} s2
|
||||
* @returns {void}
|
||||
*/
|
||||
function test_fn(s1, s2) {
|
||||
builtin.print((s1 === s2 ? "true" : "false"));
|
||||
builtin.print("\t=> ");
|
||||
builtin.println(`"${s1}", "${s2}"`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function simple_string_interpolation() {
|
||||
/** @type {string} */
|
||||
const a = "Hello";
|
||||
/** @type {string} */
|
||||
const b = "World";
|
||||
/** @type {string} */
|
||||
const res = `${a} ${b}`;
|
||||
test_fn(res, "Hello World");
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function mixed_string_interpolation() {
|
||||
/** @type {number} */
|
||||
const num = 7;
|
||||
/** @type {string} */
|
||||
const str = "abc";
|
||||
/** @type {string} */
|
||||
const s1 = `number=${num}`;
|
||||
test_fn(s1, "number=7");
|
||||
/** @type {string} */
|
||||
const s2 = `string=${str}`;
|
||||
test_fn(s2, "string=abc");
|
||||
/** @type {string} */
|
||||
const s3 = `a: ${num} | b: ${str}`;
|
||||
test_fn(s3, "a: 7 | b: abc");
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function formatted_string_interpolation() {
|
||||
/** @type {string} */
|
||||
const x = "abc";
|
||||
/** @type {string} */
|
||||
const axb = `a:${x}:b`;
|
||||
test_fn(axb, "a:abc:b");
|
||||
/** @type {string} */
|
||||
const x_10 = `a:${x}:b`;
|
||||
/** @type {string} */
|
||||
const x10_ = `a:${x}:b`;
|
||||
test_fn(x_10, "a: abc:b");
|
||||
test_fn(x10_, "a:abc :b");
|
||||
/** @type {number} */
|
||||
const i = 23;
|
||||
/** @type {string} */
|
||||
const si_right = `${i}`;
|
||||
/** @type {string} */
|
||||
const si__left = `${i}`;
|
||||
test_fn(si_right, " 23");
|
||||
test_fn(si__left, "23 ");
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function implicit_str() {
|
||||
/** @type {number} */
|
||||
const i = 42;
|
||||
test_fn(`int ${i}`, "int 42");
|
||||
test_fn(`${i}`, "42");
|
||||
/** @type {boolean} */
|
||||
const check = `${i}` === "42";
|
||||
/** @type {string} */
|
||||
const text = `${i}` + "42";
|
||||
test_fn(text, "4242");
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function string_interpolation_percent_escaping() {
|
||||
/** @type {string} */
|
||||
const test = "hello";
|
||||
/** @type {string} */
|
||||
const hello = "world";
|
||||
/** @type {string} */
|
||||
const x = `%.*s${hello}${test} |${hello}|`;
|
||||
test_fn(x, "%.*sworldhello |world |");
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function string_interpolation_string_prefix() {
|
||||
/** @type {string} */
|
||||
const r = "r";
|
||||
/** @type {string} */
|
||||
const rr = `${r}${r}`;
|
||||
test_fn(rr, "rr");
|
||||
/** @type {string} */
|
||||
const c = "c";
|
||||
/** @type {string} */
|
||||
const cc = `${c}${c}`;
|
||||
test_fn(cc, "cc");
|
||||
/** @type {string} */
|
||||
const js = "js";
|
||||
/** @type {string} */
|
||||
const jsjs = `${js}${js}`;
|
||||
test_fn(jsjs, "jsjs");
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function interpolation_string_prefix_expr() {
|
||||
/** @type {number} */
|
||||
const r = 1;
|
||||
/** @type {number} */
|
||||
const c = 2;
|
||||
/** @type {number} */
|
||||
const js = 1;
|
||||
test_fn(`>${3 + r}<`, ">4<");
|
||||
test_fn(`${r === js} ${js}`, "true 1");
|
||||
test_fn(`>${js + c} ${js + r === c}<`, ">3 true<");
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function utf8_string_interpolation() {
|
||||
/** @type {string} */
|
||||
const a = "à-côté";
|
||||
/** @type {string} */
|
||||
const st = "Sträßle";
|
||||
/** @type {string} */
|
||||
const m = "10€";
|
||||
test_fn(`${a} ${st} ${m}`, "à-côté Sträßle 10€");
|
||||
/** @type {string} */
|
||||
const zz = `>${a}< >${st}< >${m}<-`;
|
||||
/** @type {string} */
|
||||
const zz_expected = "> à-côté< >Sträßle < > 10€<-";
|
||||
test_fn(zz, zz_expected);
|
||||
/** @type {string} */
|
||||
const e = "€";
|
||||
test_fn(`100.00 ${e}`, "100.00 €");
|
||||
/** @type {string} */
|
||||
const m2 = "Москва́";
|
||||
/** @type {string} */
|
||||
const d = "Antonín Dvořák";
|
||||
test_fn(`:${m2}:${d}:`, ": Москва́:Antonín Dvořák :");
|
||||
/** @type {string} */
|
||||
const g = "Πελοπόννησος";
|
||||
test_fn(`>${g}<`, ">Πελοπόννησος <");
|
||||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {{v1?: number, v2?: number}} init
|
||||
*/
|
||||
function Sss({ v1 = 0, v2 = 0 }) {
|
||||
this.v1 = v1
|
||||
this.v2 = v2
|
||||
};
|
||||
Sss.prototype = {
|
||||
/** @type {number} */
|
||||
v1: 0,
|
||||
/** @type {number} */
|
||||
v2: 0,
|
||||
/**
|
||||
* @function
|
||||
* @returns {string}
|
||||
*/
|
||||
str() {
|
||||
const s = this;
|
||||
return `[${s.v1}, ${s.v2}]`;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {void}
|
||||
*/
|
||||
function string_interpolation_str_evaluation() {
|
||||
/** @type {Sss} */
|
||||
let x = new Sss({
|
||||
v1: 17,
|
||||
v2: 13.455893
|
||||
});
|
||||
test_fn(`${x.str()}`, "[17, 13.456]");
|
||||
}
|
||||
|
||||
/* program entry point */
|
||||
(function() {
|
||||
simple_string_interpolation();
|
||||
mixed_string_interpolation();
|
||||
formatted_string_interpolation();
|
||||
implicit_str();
|
||||
string_interpolation_percent_escaping();
|
||||
string_interpolation_string_prefix();
|
||||
interpolation_string_prefix_expr();
|
||||
utf8_string_interpolation();
|
||||
string_interpolation_str_evaluation();
|
||||
})();
|
||||
|
||||
/* module exports */
|
||||
return {};
|
||||
})();
|
||||
|
||||
|
188
vlib/v/gen/js/tests/interp.v
Normal file
188
vlib/v/gen/js/tests/interp.v
Normal file
@ -0,0 +1,188 @@
|
||||
fn test_fn(s1, s2 string) {
|
||||
print(if s1 == s2 {'true'} else {'false'})
|
||||
print('\t=> ')
|
||||
println('"$s1", "$s2"')
|
||||
}
|
||||
|
||||
fn simple_string_interpolation() {
|
||||
a := 'Hello'
|
||||
b := 'World'
|
||||
res := '$a $b'
|
||||
test_fn(res, 'Hello World')
|
||||
}
|
||||
|
||||
fn mixed_string_interpolation() {
|
||||
num := 7
|
||||
str := 'abc'
|
||||
s1 := 'number=$num'
|
||||
test_fn(s1, 'number=7')
|
||||
s2 := 'string=$str'
|
||||
test_fn(s2, 'string=abc')
|
||||
s3 := 'a: $num | b: $str'
|
||||
test_fn(s3, 'a: 7 | b: abc')
|
||||
}
|
||||
|
||||
fn formatted_string_interpolation() {
|
||||
x := 'abc'
|
||||
axb := 'a:$x:b'
|
||||
test_fn(axb, 'a:abc:b')
|
||||
x_10 := 'a:${x:10s}:b'
|
||||
x10_ := 'a:${x:-10s}:b'
|
||||
test_fn(x_10, 'a: abc:b')
|
||||
test_fn(x10_, 'a:abc :b')
|
||||
i := 23
|
||||
si_right := '${i:10d}'
|
||||
si__left := '${i:-10d}'
|
||||
test_fn(si_right, ' 23')
|
||||
test_fn(si__left, '23 ')
|
||||
}
|
||||
|
||||
/*
|
||||
excape_dollar_in_string()
|
||||
fn excape_dollar_in_string() {
|
||||
i := 42
|
||||
test_fn('($i)', '(42)')
|
||||
println('(\$i)'.contains('i') && !'(\$i)'.contains('42'))
|
||||
println(!'(\\$i)'.contains('i') && '(\\$i)'.contains('42') && '(\\$i)'.contains('\\'))
|
||||
println('(\\\$i)'.contains('i') && !'(\\\$i)'.contains('42') && '(\\$i)'.contains('\\'))
|
||||
println(!'(\\\\$i)'.contains('i') && '(\\\\$i)'.contains('42') && '(\\\\$i)'.contains('\\\\'))
|
||||
test_fn('(${i})', '(42)')
|
||||
println('(\${i})'.contains('i') && !'(\${i})'.contains('42'))
|
||||
println(!'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\'))
|
||||
println('(\\\${i})'.contains('i') && !'(\\\${i})'.contains('42') && '(\\${i})'.contains('\\'))
|
||||
println(!'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') && '(\\\\${i})'.contains('\\\\'))
|
||||
test_fn(i, 42)
|
||||
}
|
||||
*/
|
||||
|
||||
fn implicit_str() {
|
||||
i := 42
|
||||
test_fn('int $i', 'int 42')
|
||||
test_fn('$i', '42')
|
||||
check := '$i' == '42'
|
||||
//println(check)
|
||||
text := '$i' + '42'
|
||||
test_fn(text, '4242')
|
||||
}
|
||||
|
||||
fn string_interpolation_percent_escaping() {
|
||||
test := 'hello'
|
||||
hello := 'world'
|
||||
x := '%.*s$hello$test |${hello:-30s}|'
|
||||
test_fn(x, '%.*sworldhello |world |')
|
||||
}
|
||||
|
||||
fn string_interpolation_string_prefix() {
|
||||
// `r`, `c` and `js` are also used as a string prefix.
|
||||
r := 'r'
|
||||
rr := '$r$r'
|
||||
test_fn(rr, 'rr')
|
||||
c := 'c'
|
||||
cc := '$c$c'
|
||||
test_fn(cc, 'cc')
|
||||
js := 'js'
|
||||
jsjs := '$js$js'
|
||||
test_fn(jsjs, 'jsjs')
|
||||
}
|
||||
|
||||
fn interpolation_string_prefix_expr() {
|
||||
r := 1
|
||||
c := 2
|
||||
js := 1
|
||||
test_fn('>${3+r}<', '>4<')
|
||||
test_fn('${r == js} $js', 'true 1')
|
||||
test_fn('>${js+c} ${js+r==c}<', '>3 true<')
|
||||
}
|
||||
|
||||
/*
|
||||
inttypes_string_interpolation()
|
||||
fn inttypes_string_interpolation() {
|
||||
c := i8(-103)
|
||||
uc := byte(217)
|
||||
uc2 := byte(13)
|
||||
s := i16(-23456)
|
||||
us := u16(54321)
|
||||
i := -1622999040
|
||||
ui := u32(3421958087)
|
||||
vp := voidptr(ui)
|
||||
bp := byteptr(15541149836)
|
||||
l := i64(-7694555558525237396)
|
||||
ul := u64(17234006112912956370)
|
||||
test_fn('$s $us', '-23456 54321')
|
||||
test_fn('$ui $i', '3421958087 -1622999040')
|
||||
test_fn('$l $ul', '-7694555558525237396 17234006112912956370')
|
||||
test_fn('>${s:11}:${us:-13}<', '> -23456:54321 <')
|
||||
test_fn('0x${ul:-19x}:${l:22d}', '0xef2b7d4001165bd2 : -7694555558525237396')
|
||||
test_fn('${c:5}${uc:-7}x', ' -103217 x')
|
||||
test_fn('${c:x}:${uc:x}:${uc2:02X}', '99:d9:0D')
|
||||
test_fn('${s:X}:${us:x}:${u16(uc):04x}', 'A460:d431:00d9')
|
||||
test_fn('${i:x}:${ui:X}:${int(s):x}', '9f430000:CBF6EFC7:ffffa460')
|
||||
test_fn('${l:x}:${ul:X}', '9537727cad98876c:EF2B7D4001165BD2')
|
||||
// default pointer format is platform dependent, so try a few
|
||||
println("platform pointer format: '${vp:p}:$bp'")
|
||||
test_fn('${vp:p}:$bp', '0xcbf6efc7:0x39e53208c' ||
|
||||
'${vp:p}:$bp' == 'CBF6EFC7:39E53208C' ||
|
||||
'${vp:p}:$bp' == 'cbf6efc7:39e53208c' ||
|
||||
'${vp:p}:$bp' == '00000000CBF6EFC7:000000039E53208C')
|
||||
}
|
||||
*/
|
||||
|
||||
fn utf8_string_interpolation() {
|
||||
a := 'à-côté'
|
||||
st := 'Sträßle'
|
||||
m := '10€'
|
||||
test_fn('$a $st $m', 'à-côté Sträßle 10€')
|
||||
zz := '>${a:10}< >${st:-8}< >${m:5}<-'
|
||||
zz_expected := '> à-côté< >Sträßle < > 10€<-'
|
||||
//println(' zz: $zz')
|
||||
//println('zz_expected: $zz_expected')
|
||||
test_fn(zz, zz_expected)
|
||||
// e := '\u20AC' // Eurosign doesn' work with MSVC and tcc
|
||||
e := '€'
|
||||
test_fn('100.00 $e', '100.00 €')
|
||||
m2 := 'Москва́' // cyrillic а́: combination of U+0430 and U+0301, UTF-8: d0 b0 cc 81
|
||||
d := 'Antonín Dvořák' // latin á: U+00E1, UTF-8: c3 a1
|
||||
test_fn(':${m2:7}:${d:-15}:', ': Москва́:Antonín Dvořák :')
|
||||
g := 'Πελοπόννησος'
|
||||
test_fn('>${g:-13}<', '>Πελοπόννησος <')
|
||||
}
|
||||
|
||||
struct Sss {
|
||||
v1 int
|
||||
v2 f64
|
||||
}
|
||||
|
||||
fn (s Sss) str() string {
|
||||
return '[${s.v1}, ${s.v2:.3f}]'
|
||||
}
|
||||
|
||||
fn string_interpolation_str_evaluation() {
|
||||
mut x := Sss{17, 13.455893}
|
||||
test_fn('$x', '[17, 13.456]')
|
||||
}
|
||||
|
||||
/*
|
||||
string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting()
|
||||
fn string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting() {
|
||||
// discovered during debugging VLS
|
||||
i := 3
|
||||
input := '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
|
||||
println('---------------------------------------------------------------------------------------------')
|
||||
println('+60 ${i:10} | input.len: ${input.len:10} | ${input.bytes().hex():60} | $input')
|
||||
println('-60 ${i:10} | input.len: ${input.len:10} | ${input.bytes().hex():-60} | $input')
|
||||
println('---------------------------------------------------------------------------------------------')
|
||||
println(true)
|
||||
}
|
||||
*/
|
||||
|
||||
fn main() {
|
||||
simple_string_interpolation()
|
||||
mixed_string_interpolation()
|
||||
formatted_string_interpolation()
|
||||
implicit_str()
|
||||
string_interpolation_percent_escaping()
|
||||
string_interpolation_string_prefix()
|
||||
interpolation_string_prefix_expr()
|
||||
utf8_string_interpolation()
|
||||
string_interpolation_str_evaluation()
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// V_COMMIT_HASH 808975f
|
||||
// V_CURRENT_COMMIT_HASH 564545d
|
||||
// V_COMMIT_HASH 8a24d7d
|
||||
// V_CURRENT_COMMIT_HASH 123d788
|
||||
// Generated by the V compiler
|
||||
|
||||
"use strict";
|
||||
@ -214,10 +214,10 @@ const main = (function (hl) {
|
||||
/** @type {string} */
|
||||
const v_debugger = "JS keywords";
|
||||
/** @type {string} */
|
||||
const v_await = v_super + ": " + v_debugger;
|
||||
const v_await = `${v_super}: ${v_debugger}`;
|
||||
/** @type {string} */
|
||||
let v_finally = "implemented";
|
||||
console.log(v_await, v_finally);
|
||||
builtin.println(`${v_await} ${v_finally}`);
|
||||
/** @type {number} */
|
||||
const dun = i_am_a_const * 20;
|
||||
/** @type {string} */
|
||||
@ -226,7 +226,7 @@ const main = (function (hl) {
|
||||
}
|
||||
|
||||
for (let i = 0; i < "hello".length; ++i) {
|
||||
let x = "hello"[i];
|
||||
const x = "hello"[i];
|
||||
}
|
||||
|
||||
for (let x = 1; x < 10; ++x) {
|
||||
@ -235,7 +235,7 @@ const main = (function (hl) {
|
||||
/** @type {number[]} */
|
||||
const arr = [1, 2, 3, 4, 5];
|
||||
for (let _tmp6 = 0; _tmp6 < arr.length; ++_tmp6) {
|
||||
let i = arr[_tmp6];
|
||||
const i = arr[_tmp6];
|
||||
}
|
||||
|
||||
/** @type {Map<string, string>} */
|
||||
@ -255,7 +255,7 @@ const main = (function (hl) {
|
||||
|
||||
/** @type {(number: number) => void} */
|
||||
const fn_in_var = function (number) {
|
||||
builtin.println(tos3(`number: ${number}`));
|
||||
builtin.println(`number: ${number}`);
|
||||
};
|
||||
hl.v_debugger();
|
||||
anon_consumer(hl.excited(), function (message) {
|
||||
@ -294,7 +294,7 @@ const main = (function (hl) {
|
||||
*/
|
||||
function hello(game_on, ...dummy) {
|
||||
for (let _tmp7 = 0; _tmp7 < dummy.length; ++_tmp7) {
|
||||
let dd = dummy[_tmp7];
|
||||
const dd = dummy[_tmp7];
|
||||
/** @type {string} */
|
||||
const l = dd;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ fn main() {
|
||||
c.a.update('another update')
|
||||
println(c)
|
||||
|
||||
_ := "done"
|
||||
_ = "done"
|
||||
{
|
||||
_ = "block"
|
||||
}
|
||||
@ -57,10 +57,10 @@ fn main() {
|
||||
|
||||
debugger := 'JS keywords'
|
||||
// TODO: Implement interpolation
|
||||
await := super + ': ' + debugger
|
||||
await := '$super: $debugger'
|
||||
mut finally := 'implemented'
|
||||
|
||||
JS.console.log(await, finally)
|
||||
println('$await $finally')
|
||||
|
||||
dun := i_am_a_const * 20
|
||||
dunn := hl.hello // External constant
|
||||
|
Loading…
Reference in New Issue
Block a user