all: make comptime templates usable outside of vweb via $tmpl
							parent
							
								
									c6a8c3cad5
								
							
						
					
					
						commit
						5efd393af2
					
				|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 | ||||
| 			} | ||||
| 			if node.method_name == 'html' { | ||||
| 				return c.table.find_type_idx('vweb.Result') | ||||
| 			} else { | ||||
| 				return table.string_type | ||||
| 			} | ||||
| 			// return table.void_type
 | ||||
| 		} | ||||
| 		ast.ConcatExpr { | ||||
|  |  | |||
|  | @ -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') { | ||||
| 					if is_html { | ||||
| 						g.inside_vweb_tmpl = true | ||||
| 					} | ||||
| 					g.stmts(stmt.stmts) | ||||
| 					g.inside_vweb_tmpl = false | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		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"') | ||||
|  |  | |||
|  | @ -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
 | ||||
| 	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) | ||||
| 	p.check(.name) | ||||
| 	} | ||||
| 	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/`
 | ||||
| 		if is_html { | ||||
| 			path = os.join_path(dir, 'templates', fn_path.join('/')) | ||||
| 			path += '.html' | ||||
| 		} | ||||
| 		if !os.exists(path) { | ||||
| 			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 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,9 @@ import benchmark | |||
| 
 | ||||
| fn test_all_v_prod_files() { | ||||
| 	// TODO: Fix running this test on Windows:
 | ||||
| 	$if !windows { | ||||
| 	$if windows { | ||||
| 		return | ||||
| 	} | ||||
| 	options := runner.new_prod_options() | ||||
| 	mut bmark := benchmark.new_benchmark() | ||||
| 	for file in options.files { | ||||
|  | @ -23,5 +25,4 @@ fn test_all_v_prod_files() { | |||
| 	} | ||||
| 	bmark.stop() | ||||
| 	println(bmark.total_message('total time spent running PROD files')) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,9 @@ | |||
| name: @name | ||||
| 
 | ||||
| age: @age | ||||
| 
 | ||||
| numbers: @numbers | ||||
| 
 | ||||
| @for number in numbers | ||||
| @number | ||||
| @end | ||||
|  | @ -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' | ||||
| } | ||||
		Loading…
	
		Reference in New Issue