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
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			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
 | 
								// 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') {
 | 
				
			||||||
					g.inside_vweb_tmpl = true
 | 
										if is_html {
 | 
				
			||||||
 | 
											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
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		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
 | 
							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'
 | 
				
			||||||
	p.check(.dot)
 | 
						if p.peek_tok.kind == .dot {
 | 
				
			||||||
	p.check(.name)
 | 
							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)
 | 
						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/`
 | 
				
			||||||
		path = os.join_path(dir, 'templates', fn_path.join('/'))
 | 
							if is_html {
 | 
				
			||||||
		path += '.html'
 | 
								path = os.join_path(dir, 'templates', fn_path.join('/'))
 | 
				
			||||||
 | 
								path += '.html'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if !os.exists(path) {
 | 
							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"')
 | 
							// 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,23 +5,24 @@ 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 {
 | 
				
			||||||
		options := runner.new_prod_options()
 | 
							return
 | 
				
			||||||
		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'))
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						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