diff --git a/examples/tetris/tetris.v b/examples/tetris/tetris.v index baf87ed3b4..ca4bba912e 100644 --- a/examples/tetris/tetris.v +++ b/examples/tetris/tetris.v @@ -333,7 +333,7 @@ fn (g &Game) draw_tetro() { fn (g &Game) draw_next_tetro() { if g.state != .gameover { idx := g.next_tetro_idx * tetro_size * tetro_size - next_tetro := g.tetros_cache[idx..idx + tetro_size] + next_tetro := g.tetros_cache[idx..idx + tetro_size].clone() pos_y := 0 pos_x := field_width / 2 - tetro_size / 2 for i in 0 .. tetro_size { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 648f6a53d3..531bf61014 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1823,20 +1823,20 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T // TODO: non deferred pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { c.expected_type = c.cur_fn.return_type - if return_stmt.exprs.len > 0 && c.expected_type == table.void_type { + expected_type := c.unwrap_generic(c.expected_type) + expected_type_sym := c.table.get_type_symbol(expected_type) + if return_stmt.exprs.len > 0 && c.cur_fn.return_type == table.void_type { c.error('too many arguments to return, current function does not return anything', return_stmt.pos) return } else if return_stmt.exprs.len == 0 && !(c.expected_type == table.void_type || - c.table.get_type_symbol(c.expected_type).kind == .void) { + expected_type_sym.kind == .void) { c.error('too few arguments to return', return_stmt.pos) return } if return_stmt.exprs.len == 0 { return } - expected_type := c.unwrap_generic(c.expected_type) - expected_type_sym := c.table.get_type_symbol(expected_type) exp_is_optional := expected_type.has_flag(.optional) mut expected_types := [expected_type] if expected_type_sym.kind == .multi_return { @@ -2879,7 +2879,11 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { c.nr_warnings += c2.nr_warnings c.nr_errors += c2.nr_errors } - return c.table.find_type_idx('vweb.Result') + if node.method_name == 'html' { + return c.table.find_type_idx('vweb.Result') + } else { + return table.string_type + } // return table.void_type } ast.ConcatExpr { diff --git a/vlib/v/gen/comptime.v b/vlib/v/gen/comptime.v index 1243bdbe4d..3de5648fa0 100644 --- a/vlib/v/gen/comptime.v +++ b/vlib/v/gen/comptime.v @@ -9,18 +9,28 @@ import v.util fn (mut g Gen) comptime_call(node ast.ComptimeCall) { if node.is_vweb { + is_html := node.method_name == 'html' for stmt in node.vweb_tmpl.stmts { if stmt is ast.FnDecl { // insert stmts from vweb_tmpl fn if stmt.name.starts_with('main.vweb_tmpl') { - g.inside_vweb_tmpl = true + if is_html { + g.inside_vweb_tmpl = true + } g.stmts(stmt.stmts) g.inside_vweb_tmpl = false break } } } - g.writeln('vweb__Context_html(&app->vweb, _tmpl_res_$g.fn_decl.name); strings__Builder_free(&sb); string_free(&_tmpl_res_$g.fn_decl.name);') + if is_html { + // return vweb html template + g.writeln('vweb__Context_html(&app->vweb, _tmpl_res_$g.fn_decl.name); strings__Builder_free(&sb); string_free(&_tmpl_res_$g.fn_decl.name);') + } else { + // return $tmpl string + fn_name := g.fn_decl.name.replace('.', '__') + g.writeln('return _tmpl_res_$fn_name;') + } return } g.writeln('// $' + 'method call. sym="$node.sym.name"') diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 0e4d68011b..de3c61af4d 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -46,32 +46,58 @@ fn (mut p Parser) hash() ast.HashStmt { fn (mut p Parser) vweb() ast.ComptimeCall { p.check(.dollar) - p.check(.name) // skip `vweb.html()` TODO - p.check(.dot) - p.check(.name) + error_msg := 'only `\$tmpl()` and `\$vweb.html()` comptime functions are supported right now' + if p.peek_tok.kind == .dot { + n := p.check_name() // skip `vweb.html()` TODO + if n != 'vweb' { + p.error(error_msg) + } + p.check(.dot) + } + n := p.check_name() // (.name) + if n != 'html' && n != 'tmpl' { + p.error(error_msg) + } + is_html := n == 'html' p.check(.lpar) + s := if is_html { '' } else { p.tok.lit } + if !is_html { + p.check(.string) + } + println('SSSS "$s"') p.check(.rpar) // Compile vweb html template to V code, parse that V code and embed the resulting V function // that returns an html string. fn_path := p.cur_fn_name.split('_') - html_name := '${fn_path.last()}.html' + tmpl_path := if is_html { '${fn_path.last()}.html' } else { s } // Looking next to the vweb program dir := os.dir(p.scanner.file_path) mut path := os.join_path(dir, fn_path.join('/')) path += '.html' + if !is_html { + path = tmpl_path + } if !os.exists(path) { // can be in `templates/` - path = os.join_path(dir, 'templates', fn_path.join('/')) - path += '.html' + if is_html { + path = os.join_path(dir, 'templates', fn_path.join('/')) + path += '.html' + } if !os.exists(path) { - p.error('vweb HTML template "$path" not found') + if is_html { + p.error('vweb HTML template "$path" not found') + } else { + p.error('template file "$path" not found') + } } // println('path is now "$path"') } - if p.pref.is_verbose { - println('>>> compiling vweb HTML template "$path"') + if true || p.pref.is_verbose { + println('>>> compiling comptime template file "$path"') } - v_code := tmpl.compile_file(path, p.cur_fn_name) + tmp_fn_name := p.cur_fn_name.replace('.', '__') + v_code := tmpl.compile_file(path, tmp_fn_name) + println('done') $if print_vweb_template_expansions ? { lines := v_code.split('\n') for i, line in lines { @@ -82,22 +108,22 @@ fn (mut p Parser) vweb() ast.ComptimeCall { start_pos: 0 parent: p.global_scope } - mut file := parse_text(v_code, p.table, p.pref, scope, p.global_scope) - if p.pref.is_verbose { + if true || p.pref.is_verbose { println('\n\n') println('>>> vweb template for $path:') println(v_code) println('>>> end of vweb template END') println('\n\n') } + mut file := parse_text(v_code, p.table, p.pref, scope, p.global_scope) file = { file | - path: html_name + path: tmpl_path } // copy vars from current fn scope into vweb_tmpl scope for stmt in file.stmts { if stmt is ast.FnDecl { - if stmt.name == 'main.vweb_tmpl_$p.cur_fn_name' { + if stmt.name == 'main.vweb_tmpl_$tmp_fn_name' { mut tmpl_scope := file.scope.innermost(stmt.body_pos.pos) for _, obj in p.scope.objects { if obj is ast.Var { @@ -105,7 +131,7 @@ fn (mut p Parser) vweb() ast.ComptimeCall { v.pos = stmt.body_pos tmpl_scope.register(v.name, v) // set the controller action var to used - // if its unused in the template it will warn + // if it's unused in the template it will warn v.is_used = true } } @@ -116,6 +142,7 @@ fn (mut p Parser) vweb() ast.ComptimeCall { return ast.ComptimeCall{ is_vweb: true vweb_tmpl: file + method_name: n } } diff --git a/vlib/v/tests/prod_test.v b/vlib/v/tests/prod_test.v index 81c2b56521..a59ca946a7 100644 --- a/vlib/v/tests/prod_test.v +++ b/vlib/v/tests/prod_test.v @@ -5,23 +5,24 @@ import benchmark fn test_all_v_prod_files() { // TODO: Fix running this test on Windows: - $if !windows { - options := runner.new_prod_options() - mut bmark := benchmark.new_benchmark() - for file in options.files { - // println('file:$file') - bmark.step() - fres := runner.run_prod_file(options.wd, options.vexec, file) or { - bmark.fail() - eprintln(bmark.step_message_fail(err)) - assert false - continue - } - bmark.ok() - println(bmark.step_message_ok(fres)) - assert true - } - bmark.stop() - println(bmark.total_message('total time spent running PROD files')) + $if windows { + return } + options := runner.new_prod_options() + mut bmark := benchmark.new_benchmark() + for file in options.files { + // println('file:$file') + bmark.step() + fres := runner.run_prod_file(options.wd, options.vexec, file) or { + bmark.fail() + eprintln(bmark.step_message_fail(err)) + assert false + continue + } + bmark.ok() + println(bmark.step_message_ok(fres)) + assert true + } + bmark.stop() + println(bmark.total_message('total time spent running PROD files')) } diff --git a/vlib/v/tests/tmpl/1.txt b/vlib/v/tests/tmpl/1.txt new file mode 100644 index 0000000000..aeac0f3891 --- /dev/null +++ b/vlib/v/tests/tmpl/1.txt @@ -0,0 +1,9 @@ +name: @name + +age: @age + +numbers: @numbers + +@for number in numbers +@number +@end diff --git a/vlib/v/tests/tmpl_test.v b/vlib/v/tests/tmpl_test.v new file mode 100644 index 0000000000..0a6711ce13 --- /dev/null +++ b/vlib/v/tests/tmpl_test.v @@ -0,0 +1,21 @@ +fn one() string { + name := 'Peter' + age := 25 + numbers := [1, 2, 3] + return $tmpl('tmpl/1.txt') +} + +fn test_tmpl() { + assert one().trim_space() == 'name: Peter + +age: 25 + +numbers: [1, 2, 3] + + +1 + +2 + +3' +}