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 |
StringLiteral | StructInit | Type | TypeOf
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf |
ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt |
GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module |
Return | SqlStmt | StructDecl | TypeDecl | UnsafeStmt
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompFor |
CompIf | ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt |
ForStmt | GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl |
Module | Return | SqlStmt | StructDecl | TypeDecl | UnsafeStmt
pub type ScopeObject = ConstField | GlobalDecl | Var
@ -457,11 +457,11 @@ pub mut:
pub struct MatchBranch {
pub:
exprs []Expr // left side
stmts []Stmt // right side
pos token.Position
comment Comment // comment above `xxx {`
is_else bool
exprs []Expr // left side
stmts []Stmt // right side
pos token.Position
comment Comment // comment above `xxx {`
is_else bool
post_comments []Comment
}
@ -486,6 +486,15 @@ pub mut:
else_stmts []Stmt
}
pub struct CompFor {
pub:
val_var string
stmts []Stmt
pub mut:
// expr Expr
typ table.Type
}
pub struct ForStmt {
pub:
cond Expr
@ -594,12 +603,12 @@ pub:
pub struct EnumDecl {
pub:
name string
is_pub bool
is_flag bool // true when the enum has [flag] tag
name string
is_pub bool
is_flag bool // true when the enum has [flag] tag
comments []Comment // enum Abc { /* comments */ ... }
fields []EnumField
pos token.Position
fields []EnumField
pos token.Position
}
pub struct AliasTypeDecl {
@ -799,6 +808,7 @@ pub:
left Expr
is_vweb bool
vweb_tmpl File
args_var string
pub mut:
sym table.TypeSymbol
}
@ -876,7 +886,7 @@ pub fn (expr Expr) position() token.Position {
AsCast {
return expr.pos
}
// ast.Ident { }
// ast.Ident { }
CastExpr {
return expr.pos
}
@ -904,7 +914,7 @@ pub fn (expr Expr) position() token.Position {
IfExpr {
return expr.pos
}
// ast.IfGuardExpr { }
// ast.IfGuardExpr { }
IndexExpr {
return expr.pos
}
@ -935,12 +945,11 @@ pub fn (expr Expr) position() token.Position {
PostfixExpr {
return expr.pos
}
// ast.None { }
// ast.None { }
PrefixExpr {
return expr.pos
}
// ast.ParExpr { }
// ast.ParExpr { }
SelectorExpr {
return expr.pos
}
@ -953,14 +962,14 @@ pub fn (expr Expr) position() token.Position {
StringInterLiteral {
return expr.pos
}
// ast.Type { }
// ast.Type { }
StructInit {
return expr.pos
}
Likely {
return expr.pos
}
// ast.TypeOf { }
// ast.TypeOf { }
else {
return token.Position{}
}
@ -971,29 +980,29 @@ pub fn (stmt Stmt) position() token.Position {
match stmt {
AssertStmt { return stmt.pos }
AssignStmt { return stmt.pos }
/*
// Attr {
/*
// Attr {
// }
// Block {
// }
// BranchStmt {
// }
*/
*/
Comment { return stmt.pos }
CompIf { return stmt.pos }
ConstDecl { return stmt.pos }
/*
// DeferStmt {
/*
// DeferStmt {
// }
*/
*/
EnumDecl { return stmt.pos }
ExprStmt { return stmt.pos }
FnDecl { return stmt.pos }
ForCStmt { return stmt.pos }
ForInStmt { return stmt.pos }
ForStmt { return stmt.pos }
/*
// GlobalDecl {
/*
// GlobalDecl {
// }
// GoStmt {
// }
@ -1003,23 +1012,23 @@ pub fn (stmt Stmt) position() token.Position {
// }
// HashStmt {
// }
*/
*/
Import { return stmt.pos }
/*
// InterfaceDecl {
/*
// InterfaceDecl {
// }
// Module {
// }
*/
*/
Return { return stmt.pos }
StructDecl { return stmt.pos }
/*
// TypeDecl {
/*
// TypeDecl {
// }
// UnsafeStmt {
// }
*/
//
*/
//
else { return token.Position{} }
}
}

View File

@ -105,9 +105,8 @@ pub fn (mut c Checker) check2(ast_file ast.File) []errors.Error {
pub fn (mut c Checker) check_files(ast_files []ast.File) {
mut has_main_mod_file := false
mut has_main_fn := false
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]
c.check(file)
if file.mod.name == 'main' {
@ -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 c.pref.is_script && !c.pref.is_test {
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
}
}
// Make sure fn main is defined in non lib builds
if c.pref.build_mode == .build_module || c.pref.is_test {
return
@ -163,14 +160,14 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool {
c.warn('const $no_pub_in_main_warning', stmt.pos)
}
}
/*
// TODO not a Stmt
/*
// TODO not a Stmt
ast.ConstField {
if stmt.is_pub {
c.warn('const field `$stmt.name` $no_pub_in_main_warning', stmt.pos)
}
}
*/
*/
ast.EnumDecl {
if stmt.is_pub {
c.warn('enum `$stmt.name` $no_pub_in_main_warning', stmt.pos)
@ -232,7 +229,7 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool {
}
fn (mut c Checker) check_valid_snake_case(name, identifier string, pos token.Position) {
if !c.pref.is_vweb && ( name[0] == `_` || name.contains('._') ) {
if !c.pref.is_vweb && (name[0] == `_` || name.contains('._')) {
c.error('$identifier `$name` cannot start with `_`', pos)
}
if util.contains_capital(name) {
@ -378,7 +375,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
.placeholder {
c.error('unknown struct: $type_sym.name', struct_init.pos)
}
// string & array are also structs but .kind of string/array
// string & array are also structs but .kind of string/array
.struct_, .string, .array, .alias {
mut info := table.Struct{}
if type_sym.kind == .alias {
@ -1044,10 +1041,10 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
if rts_info.generic_types.len > 0 {
// TODO: multiple generic types
// for gt in rts_info.generic_types {
// gtss := c.table.get_type_symbol(gt)
// gtss := c.table.get_type_symbol(gt)
// }
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]
if idx == 0 {
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)
}
}
}
else {
} else {
call_expr.return_type = f.return_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)
if (left_type.is_ptr() || left_sym.is_pointer()) &&
assign_stmt.op !in [.assign, .decl_assign] && !c.inside_unsafe {
c.error('pointer arithmetic is only allowed in `unsafe` blocks',
assign_stmt.pos)
c.error('pointer arithmetic is only allowed in `unsafe` blocks', assign_stmt.pos)
}
// Single side check
match assign_stmt.op {
@ -1732,7 +1727,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
node.pos)
}
}
// ast.Attr {}
// ast.Attr {}
ast.AssignStmt {
c.assign_stmt(mut node)
}
@ -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())
}
}
ast.CompFor {
// node.typ = c.expr(node.expr)
c.stmts(node.stmts)
}
ast.CompIf {
// c.expr(it.cond)
c.stmts(node.stmts)
@ -1897,7 +1896,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
}
}
}
// ast.HashStmt {}
// ast.HashStmt {}
ast.Import {}
ast.InterfaceDecl {
c.interface_decl(it)
@ -2317,7 +2316,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
// main.compare_f32 may actually be builtin.compare_f32
saved_mod := ident.mod
ident.mod = 'builtin'
builtin_type := c.ident( ident )
builtin_type := c.ident(ident)
if builtin_type != table.void_type {
return builtin_type
}
@ -2446,7 +2445,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol
unhandled << '`$v_str`'
}
} }
//
//
table.Enum { for v in it.vals {
if v !in branch_exprs {
is_exhaustive = false
@ -2580,8 +2579,7 @@ pub fn (mut c Checker) postfix_expr(node ast.PostfixExpr) table.Type {
c.fail_if_immutable(node.expr)
}
if (typ.is_ptr() || typ_sym.is_pointer()) && !c.inside_unsafe {
c.error('pointer arithmetic is only allowed in `unsafe` blocks',
node.pos)
c.error('pointer arithmetic is only allowed in `unsafe` blocks', node.pos)
}
return typ
}

View File

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

View File

@ -94,6 +94,7 @@ mut:
inside_call bool
has_main bool
inside_const bool
comp_for_method string // $for method in T {
}
const (
@ -625,6 +626,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// }
}
ast.Comment {}
ast.CompFor {
g.comp_for(node)
}
ast.CompIf {
g.comp_if(node)
}
@ -1160,7 +1164,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
is_call = true
return_type = val.return_type
}
// TODO: no buffer fiddling
// TODO: no buffer fiddling
ast.AnonFn {
if blank_assign {
g.write('{')
@ -3044,7 +3048,7 @@ fn (g Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol {
field_deps << dep
}
}
// table.Interface {}
// table.Interface {}
else {}
}
// add type and dependant types to graph
@ -3504,11 +3508,11 @@ fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) string {
'linux_or_macos' {
return ''
}
//
//
'js' {
return '_VJS'
}
// compilers:
// compilers:
'tinyc' {
return '__TINYC__'
}
@ -3524,7 +3528,7 @@ fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) string {
'cplusplus' {
return '__cplusplus'
}
// other:
// other:
'debug' {
return '_VDEBUG'
}

View File

@ -27,11 +27,40 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) {
g.writeln('// $' + 'method call. sym="$node.sym.name"')
mut j := 0
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 {
// if method.return_type != table.void_type {
if method.return_type != result_type {
continue
}
if method.args.len != 1 {
continue
}
// receiver := method.args[0]
// if !p.expr_var.ptr {
// 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')
}
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 {
// Skip: don't generate comments
}
ast.CompFor {
}
ast.CompIf {
// 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('\n\n')
}
file = {file| path:html_name}
file = {
file |
path: html_name
}
// copy vars from current fn scope into vweb_tmpl scope
for stmt in file.stmts {
if stmt is 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)
for _, obj in p.scope.objects {
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 {
pos := p.tok.position()
p.next()
@ -159,15 +188,13 @@ fn (mut p Parser) comp_if() ast.Stmt {
mut skip := false
if val in supported_platforms {
os := os_from_string(val)
if (!is_not && os != p.pref.os) ||
(is_not && os == p.pref.os) {
if (!is_not && os != p.pref.os) || (is_not && os == p.pref.os) {
skip = true
}
} else if val in supported_ccompilers {
cc := cc_from_string(val)
user_cc := cc_from_string(p.pref.ccompiler)
if (!is_not && cc != user_cc) ||
(is_not && cc == user_cc) {
if (!is_not && cc != user_cc) || (is_not && cc == user_cc) {
skip = true
}
}
@ -215,8 +242,7 @@ fn (mut p Parser) comp_if() ast.Stmt {
val: val
stmts: stmts
}
if p.tok.kind == .dollar &&
p.peek_tok.kind == .key_else {
if p.tok.kind == .dollar && p.peek_tok.kind == .key_else {
p.next()
p.next()
node.has_else = true
@ -339,6 +365,11 @@ fn (mut p Parser) comptime_method_call(left ast.Expr) ast.ComptimeCall {
}
*/
p.check(.lpar)
mut args_var := ''
if p.tok.kind == .name {
args_var = p.tok.lit
p.next()
}
p.check(.rpar)
if p.tok.kind == .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{
left: left
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
ctdefine: ctdefine
mod: p.mod
attrs: p.attrs
})
} else {
if language == .c {

View File

@ -536,6 +536,8 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
.dollar {
if p.peek_tok.kind == .key_if {
return p.comp_if()
} else if p.peek_tok.kind == .key_for {
return p.comp_for()
} else if p.peek_tok.kind == .name {
return ast.ExprStmt{
expr: p.vweb()
@ -592,7 +594,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
p.error_with_pos('const can only be defined at the top level (outside of functions)',
p.tok.position())
}
// literals, 'if', etc. in here
// literals, 'if', etc. in here
else {
return p.parse_multi_expr(is_top_level)
}
@ -647,15 +649,21 @@ fn (mut p Parser) parse_attr() ast.Attr {
p.next()
is_if_attr = true
}
mut name := p.check_name()
if p.tok.kind == .colon {
name += ':'
mut name := ''
if p.tok.kind == .string {
name = p.tok.lit
p.next()
if p.tok.kind == .name {
name += p.check_name()
} else if p.tok.kind == .string {
name += p.tok.lit
} else {
mut name = p.check_name()
if p.tok.kind == .colon {
name += ':'
p.next()
if p.tok.kind == .name {
name += p.check_name()
} else if p.tok.kind == .string {
name += p.tok.lit
p.next()
}
}
}
if is_if_attr {
@ -847,11 +855,10 @@ pub fn (mut p Parser) name_expr() ast.Expr {
if p.tok.lit in ['r', 'c', 'js'] && p.peek_tok.kind == .string && !p.inside_str_interp {
return p.string_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
if p.peek_tok.kind == .dot && !known_var &&
(language != .v || p.known_import(p.tok.lit) ||
p.mod.all_after_last('.') == p.tok.lit) {
(language != .v || p.known_import(p.tok.lit) || p.mod.all_after_last('.') == p.tok.lit) {
if language == .c {
mod = 'C'
} 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()')
// fn call or type cast
if p.peek_tok.kind == .lpar ||
(p.peek_tok.kind == .lt && !lit0_is_capital && p.peek_tok2.kind == .name &&
p.peek_tok3.kind == .gt) {
(p.peek_tok.kind == .lt && !lit0_is_capital && p.peek_tok2.kind == .name && p.peek_tok3.kind == .gt) {
// foo() or foo<int>()
mut name := p.tok.lit
if mod.len > 0 {
@ -884,7 +890,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
name_w_mod := p.prepend_mod(name)
// type cast. TODO: finish
// 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 {
// TODO handle C.stat()
mut to_typ := p.parse_type()
@ -918,8 +925,9 @@ pub fn (mut p Parser) name_expr() ast.Expr {
// println('calling $p.tok.lit')
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 &&
!p.inside_for { // && (p.tok.lit[0].is_capital() || p.builtin_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 && !p.inside_for { // && (p.tok.lit[0].is_capital() || p.builtin_mod) {
return p.struct_init(false) // short_syntax: false
} else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) {
// `Color.green`
@ -1523,7 +1531,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
decl_pos := start_pos.extend(end_pos)
name := p.check_name()
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{}
if p.tok.kind == .assign {

View File

@ -30,6 +30,7 @@ pub:
is_deprecated bool
mod string
ctdefine string // compile time define. myflag, when [if myflag] tag
attrs []string
pub mut:
name string
}
@ -225,7 +226,9 @@ pub fn (mut t Table) register_builtin_type_symbol(typ TypeSymbol) int {
typ |
kind: existing_type.kind
}
} else {
}
//
else {
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{
headers: http.parse_headers(headers) //s.split_into_lines())
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.data="$req.data"' )
println('vweb action = "$action"')
//println('vweb action = "$action"')
}
//mut app := T{
app.vweb = Context{
@ -316,19 +309,60 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
data.free()
return
}
app.init()
// 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 {
println('action=$action')
}
app.init()
app.$action()
/*
app.$action() or {
conn.send_string(http_404) or {}
}
*/
conn.close() or {}
//app.reset()
return