vweb: ['/:arg1/:arg2/action'] attribute

pull/5638/head
Alexander Medvednikov 2020-07-03 15:10:39 +02:00
parent f03688e443
commit b7175b54eb
13 changed files with 281 additions and 100 deletions

View File

@ -249,3 +249,9 @@ fn __print_assert_failure(i &VAssertMetaInfo) {
} }
} }
} }
pub struct MethodAttr {
pub:
value string
method string
}

View File

@ -15,10 +15,10 @@ pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | C
ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral |
StringLiteral | StructInit | Type | TypeOf StringLiteral | StructInit | Type | TypeOf
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf | pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompFor |
ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | CompIf | ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt |
GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | ForStmt | GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl |
Return | SqlStmt | StructDecl | TypeDecl | UnsafeStmt Module | Return | SqlStmt | StructDecl | TypeDecl | UnsafeStmt
pub type ScopeObject = ConstField | GlobalDecl | Var pub type ScopeObject = ConstField | GlobalDecl | Var
@ -486,6 +486,15 @@ pub mut:
else_stmts []Stmt else_stmts []Stmt
} }
pub struct CompFor {
pub:
val_var string
stmts []Stmt
pub mut:
// expr Expr
typ table.Type
}
pub struct ForStmt { pub struct ForStmt {
pub: pub:
cond Expr cond Expr
@ -799,6 +808,7 @@ pub:
left Expr left Expr
is_vweb bool is_vweb bool
vweb_tmpl File vweb_tmpl File
args_var string
pub mut: pub mut:
sym table.TypeSymbol sym table.TypeSymbol
} }
@ -937,7 +947,6 @@ pub fn (expr Expr) position() token.Position {
} }
// ast.None { } // ast.None { }
PrefixExpr { PrefixExpr {
return expr.pos return expr.pos
} }
// ast.ParExpr { } // ast.ParExpr { }

View File

@ -105,7 +105,6 @@ pub fn (mut c Checker) check2(ast_file ast.File) []errors.Error {
pub fn (mut c Checker) check_files(ast_files []ast.File) { pub fn (mut c Checker) check_files(ast_files []ast.File) {
mut has_main_mod_file := false mut has_main_mod_file := false
mut has_main_fn := false mut has_main_fn := false
mut files_from_main_module := []&ast.File{} mut files_from_main_module := []&ast.File{}
for i in 0 .. ast_files.len { for i in 0 .. ast_files.len {
file := &ast_files[i] file := &ast_files[i]
@ -118,7 +117,6 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) {
} }
} }
} }
if has_main_mod_file && !has_main_fn && files_from_main_module.len > 0 { if has_main_mod_file && !has_main_fn && files_from_main_module.len > 0 {
if c.pref.is_script && !c.pref.is_test { if c.pref.is_script && !c.pref.is_test {
first_main_file := files_from_main_module[0] first_main_file := files_from_main_module[0]
@ -131,7 +129,6 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) {
has_main_fn = true has_main_fn = true
} }
} }
// Make sure fn main is defined in non lib builds // Make sure fn main is defined in non lib builds
if c.pref.build_mode == .build_module || c.pref.is_test { if c.pref.build_mode == .build_module || c.pref.is_test {
return return
@ -1047,7 +1044,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
// gtss := c.table.get_type_symbol(gt) // gtss := c.table.get_type_symbol(gt)
// } // }
gts := c.table.get_type_symbol(call_expr.generic_type) gts := c.table.get_type_symbol(call_expr.generic_type)
nrt := '${rts.name}<$gts.name>' nrt := '$rts.name<$gts.name>'
idx := c.table.type_idxs[nrt] idx := c.table.type_idxs[nrt]
if idx == 0 { if idx == 0 {
c.error('unknown type: $nrt', call_expr.pos) c.error('unknown type: $nrt', call_expr.pos)
@ -1055,8 +1052,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
call_expr.return_type = table.new_type(idx).derive(f.return_type) call_expr.return_type = table.new_type(idx).derive(f.return_type)
} }
} }
} } else {
else {
call_expr.return_type = f.return_type call_expr.return_type = f.return_type
} }
if f.return_type == table.void_type && if f.return_type == table.void_type &&
@ -1509,8 +1505,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
right_sym := c.table.get_type_symbol(right_type_unwrapped) right_sym := c.table.get_type_symbol(right_type_unwrapped)
if (left_type.is_ptr() || left_sym.is_pointer()) && if (left_type.is_ptr() || left_sym.is_pointer()) &&
assign_stmt.op !in [.assign, .decl_assign] && !c.inside_unsafe { assign_stmt.op !in [.assign, .decl_assign] && !c.inside_unsafe {
c.error('pointer arithmetic is only allowed in `unsafe` blocks', c.error('pointer arithmetic is only allowed in `unsafe` blocks', assign_stmt.pos)
assign_stmt.pos)
} }
// Single side check // Single side check
match assign_stmt.op { match assign_stmt.op {
@ -1744,6 +1739,10 @@ fn (mut c Checker) stmt(node ast.Stmt) {
c.error('$node.tok.lit statement not within a loop', node.tok.position()) c.error('$node.tok.lit statement not within a loop', node.tok.position())
} }
} }
ast.CompFor {
// node.typ = c.expr(node.expr)
c.stmts(node.stmts)
}
ast.CompIf { ast.CompIf {
// c.expr(it.cond) // c.expr(it.cond)
c.stmts(node.stmts) c.stmts(node.stmts)
@ -2580,8 +2579,7 @@ pub fn (mut c Checker) postfix_expr(node ast.PostfixExpr) table.Type {
c.fail_if_immutable(node.expr) c.fail_if_immutable(node.expr)
} }
if (typ.is_ptr() || typ_sym.is_pointer()) && !c.inside_unsafe { if (typ.is_ptr() || typ_sym.is_pointer()) && !c.inside_unsafe {
c.error('pointer arithmetic is only allowed in `unsafe` blocks', c.error('pointer arithmetic is only allowed in `unsafe` blocks', node.pos)
node.pos)
} }
return typ return typ
} }

View File

@ -290,6 +290,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
ast.Comment { ast.Comment {
f.comment(it) f.comment(it)
} }
ast.CompFor {}
ast.CompIf { ast.CompIf {
inversion := if it.is_not { '!' } else { '' } inversion := if it.is_not { '!' } else { '' }
is_opt := if it.is_opt { ' ?' } else { '' } is_opt := if it.is_opt { ' ?' } else { '' }

View File

@ -94,6 +94,7 @@ mut:
inside_call bool inside_call bool
has_main bool has_main bool
inside_const bool inside_const bool
comp_for_method string // $for method in T {
} }
const ( const (
@ -625,6 +626,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// } // }
} }
ast.Comment {} ast.Comment {}
ast.CompFor {
g.comp_for(node)
}
ast.CompIf { ast.CompIf {
g.comp_if(node) g.comp_if(node)
} }

View File

@ -27,11 +27,40 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) {
g.writeln('// $' + 'method call. sym="$node.sym.name"') g.writeln('// $' + 'method call. sym="$node.sym.name"')
mut j := 0 mut j := 0
result_type := g.table.find_type_idx('vweb.Result') // TODO not just vweb result_type := g.table.find_type_idx('vweb.Result') // TODO not just vweb
if node.method_name == 'method' {
// `app.$method()`
m := node.sym.find_method(g.comp_for_method) or {
return
}
/*
vals := m.attrs[0].split('/')
args := vals.filter(it.starts_with(':')).map(it[1..])
println(vals)
for val in vals {
}
*/
g.write('${util.no_dots(node.sym.name)}_${g.comp_for_method}(')
g.expr(node.left)
if m.args.len > 1 {
g.write(', ')
}
for i in 0 .. m.args.len - 1 {
g.write('((string*)${node.args_var}.data) [$i] ')
if i < m.args.len - 2 {
g.write(', ')
}
}
g.write(' ); // vweb action call with args')
return
}
for method in node.sym.methods { for method in node.sym.methods {
// if method.return_type != table.void_type { // if method.return_type != table.void_type {
if method.return_type != result_type { if method.return_type != result_type {
continue continue
} }
if method.args.len != 1 {
continue
}
// receiver := method.args[0] // receiver := method.args[0]
// if !p.expr_var.ptr { // if !p.expr_var.ptr {
// p.error('`$p.expr_var.name` needs to be a reference') // p.error('`$p.expr_var.name` needs to be a reference')
@ -69,3 +98,29 @@ fn (mut g Gen) comp_if(it ast.CompIf) {
} }
g.writeln('\n#endif\n// } $it.val\n') g.writeln('\n#endif\n// } $it.val\n')
} }
fn (mut g Gen) comp_for(node ast.CompFor) {
g.writeln('// comptime $' + 'for {')
sym := g.table.get_type_symbol(g.unwrap_generic(node.typ))
mut i := 0
// g.writeln('string method = tos_lit("");')
for method in sym.methods {
if method.attrs.len == 0 {
continue
}
g.comp_for_method = method.name
g.writeln('\t// method $i')
if i == 0 {
g.write('\tstring ')
}
g.writeln('method = tos_lit("$method.name");')
if i == 0 {
g.write('\tstring ')
}
g.writeln('attrs = tos_lit("${method.attrs[0]}");')
g.stmts(node.stmts)
i++
g.writeln('')
}
g.writeln('// } comptime for')
}

View File

@ -422,6 +422,8 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
ast.Comment { ast.Comment {
// Skip: don't generate comments // Skip: don't generate comments
} }
ast.CompFor {
}
ast.CompIf { ast.CompIf {
// skip: JS has no compile time if // skip: JS has no compile time if
} }

View File

@ -116,12 +116,15 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
println('>>> end of vweb template END') println('>>> end of vweb template END')
println('\n\n') println('\n\n')
} }
file = {file| path:html_name} file = {
file |
path: html_name
}
// 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 {
fn_decl := stmt as ast.FnDecl fn_decl := stmt as ast.FnDecl
if fn_decl.name == 'main.vweb_tmpl_${p.cur_fn_name}' { if fn_decl.name == 'main.vweb_tmpl_$p.cur_fn_name' {
tmpl_scope := file.scope.innermost(fn_decl.body_pos.pos) tmpl_scope := file.scope.innermost(fn_decl.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 {
@ -143,6 +146,32 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
} }
} }
fn (mut p Parser) comp_for() ast.CompFor {
println('COMP FOR')
p.next()
p.check(.key_for)
val_var := p.check_name()
p.scope.register(val_var, ast.Var{
name: val_var
typ: table.string_type
})
p.scope.register('attrs', ast.Var{
name: 'attrs'
typ: table.string_type
})
p.check(.key_in)
// expr := p.expr(0)
typ := p.parse_type()
// p.check(.dot)
// p.check_name()
stmts := p.parse_block()
return ast.CompFor{
val_var: val_var
stmts: stmts
typ: typ
}
}
fn (mut p Parser) comp_if() ast.Stmt { fn (mut p Parser) comp_if() ast.Stmt {
pos := p.tok.position() pos := p.tok.position()
p.next() p.next()
@ -159,15 +188,13 @@ fn (mut p Parser) comp_if() ast.Stmt {
mut skip := false mut skip := false
if val in supported_platforms { if val in supported_platforms {
os := os_from_string(val) os := os_from_string(val)
if (!is_not && os != p.pref.os) || if (!is_not && os != p.pref.os) || (is_not && os == p.pref.os) {
(is_not && os == p.pref.os) {
skip = true skip = true
} }
} else if val in supported_ccompilers { } else if val in supported_ccompilers {
cc := cc_from_string(val) cc := cc_from_string(val)
user_cc := cc_from_string(p.pref.ccompiler) user_cc := cc_from_string(p.pref.ccompiler)
if (!is_not && cc != user_cc) || if (!is_not && cc != user_cc) || (is_not && cc == user_cc) {
(is_not && cc == user_cc) {
skip = true skip = true
} }
} }
@ -215,8 +242,7 @@ fn (mut p Parser) comp_if() ast.Stmt {
val: val val: val
stmts: stmts stmts: stmts
} }
if p.tok.kind == .dollar && if p.tok.kind == .dollar && p.peek_tok.kind == .key_else {
p.peek_tok.kind == .key_else {
p.next() p.next()
p.next() p.next()
node.has_else = true node.has_else = true
@ -339,6 +365,11 @@ fn (mut p Parser) comptime_method_call(left ast.Expr) ast.ComptimeCall {
} }
*/ */
p.check(.lpar) p.check(.lpar)
mut args_var := ''
if p.tok.kind == .name {
args_var = p.tok.lit
p.next()
}
p.check(.rpar) p.check(.rpar)
if p.tok.kind == .key_orelse { if p.tok.kind == .key_orelse {
p.check(.key_orelse) p.check(.key_orelse)
@ -349,5 +380,6 @@ fn (mut p Parser) comptime_method_call(left ast.Expr) ast.ComptimeCall {
return ast.ComptimeCall{ return ast.ComptimeCall{
left: left left: left
method_name: method_name method_name: method_name
args_var: args_var
} }
} }

View File

@ -240,6 +240,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
is_deprecated: is_deprecated is_deprecated: is_deprecated
ctdefine: ctdefine ctdefine: ctdefine
mod: p.mod mod: p.mod
attrs: p.attrs
}) })
} else { } else {
if language == .c { if language == .c {

View File

@ -536,6 +536,8 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
.dollar { .dollar {
if p.peek_tok.kind == .key_if { if p.peek_tok.kind == .key_if {
return p.comp_if() return p.comp_if()
} else if p.peek_tok.kind == .key_for {
return p.comp_for()
} else if p.peek_tok.kind == .name { } else if p.peek_tok.kind == .name {
return ast.ExprStmt{ return ast.ExprStmt{
expr: p.vweb() expr: p.vweb()
@ -647,7 +649,12 @@ fn (mut p Parser) parse_attr() ast.Attr {
p.next() p.next()
is_if_attr = true is_if_attr = true
} }
mut name := p.check_name() mut name := ''
if p.tok.kind == .string {
name = p.tok.lit
p.next()
} else {
mut name = p.check_name()
if p.tok.kind == .colon { if p.tok.kind == .colon {
name += ':' name += ':'
p.next() p.next()
@ -658,6 +665,7 @@ fn (mut p Parser) parse_attr() ast.Attr {
p.next() p.next()
} }
} }
}
if is_if_attr { if is_if_attr {
p.attr_ctdefine = name p.attr_ctdefine = name
} }
@ -850,8 +858,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
known_var := p.mark_var_as_used(p.tok.lit) known_var := p.mark_var_as_used(p.tok.lit)
mut is_mod_cast := false mut is_mod_cast := false
if p.peek_tok.kind == .dot && !known_var && if p.peek_tok.kind == .dot && !known_var &&
(language != .v || p.known_import(p.tok.lit) || (language != .v || p.known_import(p.tok.lit) || p.mod.all_after_last('.') == p.tok.lit) {
p.mod.all_after_last('.') == p.tok.lit) {
if language == .c { if language == .c {
mod = 'C' mod = 'C'
} else if language == .js { } else if language == .js {
@ -874,8 +881,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
// p.warn('name expr $p.tok.lit $p.peek_tok.str()') // p.warn('name expr $p.tok.lit $p.peek_tok.str()')
// fn call or type cast // fn call or type cast
if p.peek_tok.kind == .lpar || if p.peek_tok.kind == .lpar ||
(p.peek_tok.kind == .lt && !lit0_is_capital && p.peek_tok2.kind == .name && (p.peek_tok.kind == .lt && !lit0_is_capital && p.peek_tok2.kind == .name && p.peek_tok3.kind == .gt) {
p.peek_tok3.kind == .gt) {
// foo() or foo<int>() // foo() or foo<int>()
mut name := p.tok.lit mut name := p.tok.lit
if mod.len > 0 { if mod.len > 0 {
@ -884,7 +890,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
name_w_mod := p.prepend_mod(name) name_w_mod := p.prepend_mod(name)
// type cast. TODO: finish // type cast. TODO: finish
// if name in table.builtin_type_names { // if name in table.builtin_type_names {
if (!known_var && (name in p.table.type_idxs || name_w_mod in p.table.type_idxs) && if (!known_var && (name in p.table.type_idxs ||
name_w_mod in p.table.type_idxs) &&
name !in ['C.stat', 'C.sigaction']) || is_mod_cast { name !in ['C.stat', 'C.sigaction']) || is_mod_cast {
// TODO handle C.stat() // TODO handle C.stat()
mut to_typ := p.parse_type() mut to_typ := p.parse_type()
@ -918,8 +925,9 @@ pub fn (mut p Parser) name_expr() ast.Expr {
// println('calling $p.tok.lit') // println('calling $p.tok.lit')
node = p.call_expr(language, mod) node = p.call_expr(language, mod)
} }
} else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital)) && !p.inside_match && !p.inside_match_case && !p.inside_if && } else if (p.peek_tok.kind == .lcbr ||
!p.inside_for { // && (p.tok.lit[0].is_capital() || p.builtin_mod) { (p.peek_tok.kind == .lt && lit0_is_capital)) && !p.inside_match && !p.inside_match_case &&
!p.inside_if && !p.inside_for { // && (p.tok.lit[0].is_capital() || p.builtin_mod) {
return p.struct_init(false) // short_syntax: false return p.struct_init(false) // short_syntax: false
} else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) { } else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) {
// `Color.green` // `Color.green`
@ -1523,7 +1531,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
decl_pos := start_pos.extend(end_pos) decl_pos := start_pos.extend(end_pos)
name := p.check_name() name := p.check_name()
if name.len == 1 && name[0].is_capital() { if name.len == 1 && name[0].is_capital() {
p.error_with_pos('single letter capital names are reserved for generic template types.', decl_pos) p.error_with_pos('single letter capital names are reserved for generic template types.',
decl_pos)
} }
mut sum_variants := []table.Type{} mut sum_variants := []table.Type{}
if p.tok.kind == .assign { if p.tok.kind == .assign {

View File

@ -30,6 +30,7 @@ pub:
is_deprecated bool is_deprecated bool
mod string mod string
ctdefine string // compile time define. myflag, when [if myflag] tag ctdefine string // compile time define. myflag, when [if myflag] tag
attrs []string
pub mut: pub mut:
name string name string
} }
@ -225,7 +226,9 @@ pub fn (mut t Table) register_builtin_type_symbol(typ TypeSymbol) int {
typ | typ |
kind: existing_type.kind kind: existing_type.kind
} }
} else { }
//
else {
t.types[existing_idx] = typ t.types[existing_idx] = typ
} }
} }

View File

@ -0,0 +1,27 @@
struct App {
}
['foo/bar/three']
fn (mut app App) run() {
}
['attr2']
fn (mut app App) method2() {
}
fn test_comptime_for() {
/*
app := App{}
$for method in App { //.method_attrs {
words := attrs.split('/')
println(words)
//println(method.value)
}
assert true
println('DONE')
*/
if true {}
//
else{}
}

View File

@ -255,13 +255,6 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
} }
} }
mut action := vals[1][1..].all_before('/')
if action.contains('?') {
action = action.all_before('?')
}
if action == '' {
action = 'index'
}
req := http.Request{ req := http.Request{
headers: http.parse_headers(headers) //s.split_into_lines()) headers: http.parse_headers(headers) //s.split_into_lines())
data: strip(body) data: strip(body)
@ -274,7 +267,7 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
println('req.headers = ') println('req.headers = ')
println(req.headers) println(req.headers)
println('req.data="$req.data"' ) println('req.data="$req.data"' )
println('vweb action = "$action"') //println('vweb action = "$action"')
} }
//mut app := T{ //mut app := T{
app.vweb = Context{ app.vweb = Context{
@ -316,19 +309,60 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
data.free() data.free()
return return
} }
app.init()
// Call the right action // Call the right action
mut action := ''
mut route_words := []string{}
mut ok := true
url_words := vals[1][1..].split('/')
mut vars := []string{cap: route_words.len}
$for method in T {
ok = true
route_words = attrs[1..].split('/')
//println('words:') println(route_words)
//println('vals:') println(url_words)
vars = []string{cap: route_words.len}
if route_words.len == url_words.len {
// match `/:user/:repo/tree` to `/vlang/v/tree`
for i, word in route_words {
if word.starts_with(':') {
// remember and skip the var
vars << url_words[i]
continue
}
if word != url_words[i] {
ok = false
break
}
}
}
if ok {
action = method
app.$method(vars)
conn.close() or {}
return
}
}
// No route matched, just do a simple `/home` => `action=home`
if action == '' {
action = vals[1][1..].all_before('/')
if action.contains('?') {
action = action.all_before('?')
}
if action == '' {
action = 'index'
}
}
$if debug { $if debug {
println('action=$action') println('action=$action')
} }
app.init()
app.$action() app.$action()
/* /*
app.$action() or { app.$action() or {
conn.send_string(http_404) or {} conn.send_string(http_404) or {}
} }
*/ */
conn.close() or {} conn.close() or {}
//app.reset() //app.reset()
return return