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
 | 
			
		||||
			}
 | 
			
		||||
			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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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