diff --git a/vlib/strings/builder_c.v b/vlib/strings/builder_c.v index 8a3a6d7846..8e36e845f7 100644 --- a/vlib/strings/builder_c.v +++ b/vlib/strings/builder_c.v @@ -55,6 +55,26 @@ pub fn (b mut Builder) writeln(s string) { b.len += s.len + 1 } +// buf == 'hello world' +// last_n(5) returns 'world' +pub fn (b mut Builder) last_n(n int) string { + if n > b.len { + return '' + } + buf := b.buf[b.len-n..] + return string(buf.clone()) +} + +// buf == 'hello world' +// after(6) returns 'world' +pub fn (b mut Builder) after(n int) string { + if n >= b.len { + return '' + } + buf := b.buf[n..] + return string(buf.clone()) +} + pub fn (b mut Builder) str() string { b.buf << `\0` return string(b.buf,b.len) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 54a63770b8..6cd29f6819 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -14,7 +14,7 @@ pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLitera FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr | AssignExpr | PrefixExpr | MethodCallExpr | IndexExpr | RangeExpr | MatchExpr | CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr | -ConcatExpr | Type | AsCast | TypeOf +ConcatExpr | Type | AsCast | TypeOf | StringInterLiteral pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt | ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt | @@ -54,6 +54,15 @@ pub: val string } +// 'name: $name' +pub struct StringInterLiteral { +pub: + vals []string + exprs []Expr +mut: + expr_types []table.Type +} + pub struct CharLiteral { pub: val string diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7356e349db..e6a4183e93 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -393,7 +393,8 @@ pub fn (c mut Checker) return_stmt(return_stmt mut ast.Return) { return } if expected_types.len > 0 && expected_types.len != got_types.len { - c.error('wrong number of return arguments:\n\texpected: $expected_types.str()\n\tgot: $got_types.str()', return_stmt.pos) + // c.error('wrong number of return arguments:\n\texpected: $expected_types.str()\n\tgot: $got_types.str()', return_stmt.pos) + c.error('wrong number of return arguments', return_stmt.pos) } for i, exp_typ in expected_types { got_typ := got_types[i] @@ -737,6 +738,12 @@ pub fn (c mut Checker) expr(node ast.Expr) table.Type { ast.StringLiteral { return table.string_type } + ast.StringInterLiteral { + for expr in it.exprs { + it.expr_types << c.expr(expr) + } + return table.string_type + } ast.StructInit { return c.struct_init(mut it) } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 2fc6dd4aa1..096c99d758 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -868,6 +868,9 @@ fn (g mut Gen) expr(node ast.Expr) { g.write('tos3("$escaped_val")') } } + ast.StringInterLiteral { + g.string_inter_literal(it) + } // `user := User{name: 'Bob'}` ast.StructInit { styp := g.typ(it.typ) @@ -1652,6 +1655,46 @@ fn (g &Gen) sort_structs(types []table.TypeSymbol) []table.TypeSymbol { return types_sorted } +fn (g mut Gen) string_inter_literal(node ast.StringInterLiteral) { + g.write('_STR("') + // Build the string with % + for i, val in node.vals { + g.write(val) + if i >= node.exprs.len { + continue + } + pos := g.out.len + match node.expr_types[i] { + table.string_type { + g.write('%.*s') + } + table.int_type { + g.write('%d') + } + else {} + } + } + g.write('", ') + // Build args + for i, expr in node.exprs { + if node.expr_types[i] == table.string_type { + // `name.str, name.len,` + g.expr(node.exprs[i]) + g.write('.len, ') + g.expr(node.exprs[i]) + g.write('.str') + } + else { + g.expr(node.exprs[i]) + } + if i < node.exprs.len - 1 { + g.write(', ') + } + } + g.write(')') +} + +// `nums.filter(it % 2 == 0)` fn (g mut Gen) gen_filter(node ast.MethodCallExpr) { tmp := g.new_tmp_var() buf := g.out.buf[g.stmt_start_pos..] diff --git a/vlib/v/gen/tests/3.c b/vlib/v/gen/tests/3.c index 5b297c264b..df41cb985d 100644 --- a/vlib/v/gen/tests/3.c +++ b/vlib/v/gen/tests/3.c @@ -37,6 +37,11 @@ tos3(""), if (a == 10 || a == 20 || a == 30) { int b = 10; } + string name = tos3("Bob"); + println(tos3("hello")); + println(_STR("Hello, %.*s", name.len, name.str)); + println(_STR("age = %d", age)); + println(_STR("name=%.*s age=%d", name.len, name.str, age)); } void println(string s) { diff --git a/vlib/v/gen/tests/3.vv b/vlib/v/gen/tests/3.vv index c6b7f5852e..4c4403145d 100644 --- a/vlib/v/gen/tests/3.vv +++ b/vlib/v/gen/tests/3.vv @@ -30,6 +30,11 @@ fn (u &User) foo() { if a in [10, 20, 30] { b := 10 } + name := 'Bob' + println('hello') + println('Hello, $name') + println('age = $age') + println('name=$name age=$age') } fn println(s string) {} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index b002490933..ba000e00c7 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1243,21 +1243,25 @@ fn (p mut Parser) if_expr() ast.IfExpr { fn (p mut Parser) string_expr() ast.Expr { mut node := ast.Expr{} + val := p.tok.lit node = ast.StringLiteral{ - val: p.tok.lit + val: val } if p.peek_tok.kind != .str_dollar { p.next() return node } + mut exprs := []ast.Expr + mut vals := []string // Handle $ interpolation for p.tok.kind == .string { + vals << p.tok.lit p.next() if p.tok.kind != .str_dollar { continue } p.check(.str_dollar) - p.expr(0) + exprs << p.expr(0) if p.tok.kind == .colon { p.next() } @@ -1273,6 +1277,10 @@ fn (p mut Parser) string_expr() ast.Expr { } } } + node = ast.StringInterLiteral{ + vals: vals + exprs: exprs + } return node }