all: make comptime templates usable outside of vweb via $tmpl

pull/6968/head
Alexander Medvednikov 2020-11-26 18:40:31 +01:00
parent c6a8c3cad5
commit 5efd393af2
7 changed files with 113 additions and 41 deletions

View File

@ -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 {

View File

@ -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 {

View File

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

View File

@ -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
}
}

View File

@ -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 {
@ -24,4 +26,3 @@ fn test_all_v_prod_files() {
bmark.stop()
println(bmark.total_message('total time spent running PROD files'))
}
}

View File

@ -0,0 +1,9 @@
name: @name
age: @age
numbers: @numbers
@for number in numbers
@number
@end

View File

@ -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'
}