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() { | fn (g &Game) draw_next_tetro() { | ||||||
| 	if g.state != .gameover { | 	if g.state != .gameover { | ||||||
| 		idx := g.next_tetro_idx * tetro_size * tetro_size | 		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_y := 0 | ||||||
| 		pos_x := field_width / 2 - tetro_size / 2 | 		pos_x := field_width / 2 - tetro_size / 2 | ||||||
| 		for i in 0 .. tetro_size { | 		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
 | // TODO: non deferred
 | ||||||
| pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { | pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { | ||||||
| 	c.expected_type = c.cur_fn.return_type | 	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', | 		c.error('too many arguments to return, current function does not return anything', | ||||||
| 			return_stmt.pos) | 			return_stmt.pos) | ||||||
| 		return | 		return | ||||||
| 	} else if return_stmt.exprs.len == 0 && !(c.expected_type == table.void_type || | 	} 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) | 		c.error('too few arguments to return', return_stmt.pos) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if return_stmt.exprs.len == 0 { | 	if return_stmt.exprs.len == 0 { | ||||||
| 		return | 		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) | 	exp_is_optional := expected_type.has_flag(.optional) | ||||||
| 	mut expected_types := [expected_type] | 	mut expected_types := [expected_type] | ||||||
| 	if expected_type_sym.kind == .multi_return { | 	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_warnings += c2.nr_warnings | ||||||
| 				c.nr_errors += c2.nr_errors | 				c.nr_errors += c2.nr_errors | ||||||
| 			} | 			} | ||||||
|  | 			if node.method_name == 'html' { | ||||||
| 				return c.table.find_type_idx('vweb.Result') | 				return c.table.find_type_idx('vweb.Result') | ||||||
|  | 			} else { | ||||||
|  | 				return table.string_type | ||||||
|  | 			} | ||||||
| 			// return table.void_type
 | 			// return table.void_type
 | ||||||
| 		} | 		} | ||||||
| 		ast.ConcatExpr { | 		ast.ConcatExpr { | ||||||
|  |  | ||||||
|  | @ -9,18 +9,28 @@ import v.util | ||||||
| 
 | 
 | ||||||
| fn (mut g Gen) comptime_call(node ast.ComptimeCall) { | fn (mut g Gen) comptime_call(node ast.ComptimeCall) { | ||||||
| 	if node.is_vweb { | 	if node.is_vweb { | ||||||
|  | 		is_html := node.method_name == 'html' | ||||||
| 		for stmt in node.vweb_tmpl.stmts { | 		for stmt in node.vweb_tmpl.stmts { | ||||||
| 			if stmt is ast.FnDecl { | 			if stmt is ast.FnDecl { | ||||||
| 				// insert stmts from vweb_tmpl fn
 | 				// insert stmts from vweb_tmpl fn
 | ||||||
| 				if stmt.name.starts_with('main.vweb_tmpl') { | 				if stmt.name.starts_with('main.vweb_tmpl') { | ||||||
|  | 					if is_html { | ||||||
| 						g.inside_vweb_tmpl = true | 						g.inside_vweb_tmpl = true | ||||||
|  | 					} | ||||||
| 					g.stmts(stmt.stmts) | 					g.stmts(stmt.stmts) | ||||||
| 					g.inside_vweb_tmpl = false | 					g.inside_vweb_tmpl = false | ||||||
| 					break | 					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);') | 			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 | 		return | ||||||
| 	} | 	} | ||||||
| 	g.writeln('// $' + 'method call. sym="$node.sym.name"') | 	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 { | fn (mut p Parser) vweb() ast.ComptimeCall { | ||||||
| 	p.check(.dollar) | 	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(.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) | 	p.check(.lpar) | ||||||
|  | 	s := if is_html { '' } else { p.tok.lit } | ||||||
|  | 	if !is_html { | ||||||
|  | 		p.check(.string) | ||||||
|  | 	} | ||||||
|  | 	println('SSSS "$s"') | ||||||
| 	p.check(.rpar) | 	p.check(.rpar) | ||||||
| 	// Compile vweb html template to V code, parse that V code and embed the resulting V function
 | 	// Compile vweb html template to V code, parse that V code and embed the resulting V function
 | ||||||
| 	// that returns an html string.
 | 	// that returns an html string.
 | ||||||
| 	fn_path := p.cur_fn_name.split('_') | 	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
 | 	// Looking next to the vweb program
 | ||||||
| 	dir := os.dir(p.scanner.file_path) | 	dir := os.dir(p.scanner.file_path) | ||||||
| 	mut path := os.join_path(dir, fn_path.join('/')) | 	mut path := os.join_path(dir, fn_path.join('/')) | ||||||
| 	path += '.html' | 	path += '.html' | ||||||
|  | 	if !is_html { | ||||||
|  | 		path = tmpl_path | ||||||
|  | 	} | ||||||
| 	if !os.exists(path) { | 	if !os.exists(path) { | ||||||
| 		// can be in `templates/`
 | 		// can be in `templates/`
 | ||||||
|  | 		if is_html { | ||||||
| 			path = os.join_path(dir, 'templates', fn_path.join('/')) | 			path = os.join_path(dir, 'templates', fn_path.join('/')) | ||||||
| 			path += '.html' | 			path += '.html' | ||||||
|  | 		} | ||||||
| 		if !os.exists(path) { | 		if !os.exists(path) { | ||||||
|  | 			if is_html { | ||||||
| 				p.error('vweb HTML template "$path" not found') | 				p.error('vweb HTML template "$path" not found') | ||||||
|  | 			} else { | ||||||
|  | 				p.error('template file "$path" not found') | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		// println('path is now "$path"')
 | 		// println('path is now "$path"')
 | ||||||
| 	} | 	} | ||||||
| 	if p.pref.is_verbose { | 	if true || p.pref.is_verbose { | ||||||
| 		println('>>> compiling vweb HTML template "$path"') | 		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 ? { | 	$if print_vweb_template_expansions ? { | ||||||
| 		lines := v_code.split('\n') | 		lines := v_code.split('\n') | ||||||
| 		for i, line in lines { | 		for i, line in lines { | ||||||
|  | @ -82,22 +108,22 @@ fn (mut p Parser) vweb() ast.ComptimeCall { | ||||||
| 		start_pos: 0 | 		start_pos: 0 | ||||||
| 		parent: p.global_scope | 		parent: p.global_scope | ||||||
| 	} | 	} | ||||||
| 	mut file := parse_text(v_code, p.table, p.pref, scope, p.global_scope) | 	if true || p.pref.is_verbose { | ||||||
| 	if p.pref.is_verbose { |  | ||||||
| 		println('\n\n') | 		println('\n\n') | ||||||
| 		println('>>> vweb template for $path:') | 		println('>>> vweb template for $path:') | ||||||
| 		println(v_code) | 		println(v_code) | ||||||
| 		println('>>> end of vweb template END') | 		println('>>> end of vweb template END') | ||||||
| 		println('\n\n') | 		println('\n\n') | ||||||
| 	} | 	} | ||||||
|  | 	mut file := parse_text(v_code, p.table, p.pref, scope, p.global_scope) | ||||||
| 	file = { | 	file = { | ||||||
| 		file | | 		file | | ||||||
| 		path: html_name | 		path: tmpl_path | ||||||
| 	} | 	} | ||||||
| 	// copy vars from current fn scope into vweb_tmpl scope
 | 	// copy vars from current fn scope into vweb_tmpl scope
 | ||||||
| 	for stmt in file.stmts { | 	for stmt in file.stmts { | ||||||
| 		if stmt is ast.FnDecl { | 		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) | 				mut tmpl_scope := file.scope.innermost(stmt.body_pos.pos) | ||||||
| 				for _, obj in p.scope.objects { | 				for _, obj in p.scope.objects { | ||||||
| 					if obj is ast.Var { | 					if obj is ast.Var { | ||||||
|  | @ -105,7 +131,7 @@ fn (mut p Parser) vweb() ast.ComptimeCall { | ||||||
| 						v.pos = stmt.body_pos | 						v.pos = stmt.body_pos | ||||||
| 						tmpl_scope.register(v.name, v) | 						tmpl_scope.register(v.name, v) | ||||||
| 						// set the controller action var to used
 | 						// 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 | 						v.is_used = true | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | @ -116,6 +142,7 @@ fn (mut p Parser) vweb() ast.ComptimeCall { | ||||||
| 	return ast.ComptimeCall{ | 	return ast.ComptimeCall{ | ||||||
| 		is_vweb: true | 		is_vweb: true | ||||||
| 		vweb_tmpl: file | 		vweb_tmpl: file | ||||||
|  | 		method_name: n | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,9 @@ import benchmark | ||||||
| 
 | 
 | ||||||
| fn test_all_v_prod_files() { | fn test_all_v_prod_files() { | ||||||
| 	// TODO: Fix running this test on Windows:
 | 	// TODO: Fix running this test on Windows:
 | ||||||
| 	$if !windows { | 	$if windows { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	options := runner.new_prod_options() | 	options := runner.new_prod_options() | ||||||
| 	mut bmark := benchmark.new_benchmark() | 	mut bmark := benchmark.new_benchmark() | ||||||
| 	for file in options.files { | 	for file in options.files { | ||||||
|  | @ -23,5 +25,4 @@ fn test_all_v_prod_files() { | ||||||
| 	} | 	} | ||||||
| 	bmark.stop() | 	bmark.stop() | ||||||
| 	println(bmark.total_message('total time spent running PROD files')) | 	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