mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
gen: compile time for (methods and fields) (#5957)
This commit is contained in:
parent
2ccb28a93e
commit
b58b15993c
45
examples/compiletime/compile-time-for.v
Normal file
45
examples/compiletime/compile-time-for.v
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
test string [test]
|
||||||
|
mut:
|
||||||
|
a string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println('All functions')
|
||||||
|
$for method in App.methods {
|
||||||
|
$if method.@type is int {
|
||||||
|
println('hi')
|
||||||
|
}
|
||||||
|
println('$method.name.len')
|
||||||
|
println('$method.name.str')
|
||||||
|
println('Method: $method.name')
|
||||||
|
println('Attributes: $method.attrs')
|
||||||
|
println('Return type: $method.ret_type')
|
||||||
|
}
|
||||||
|
println('All integer functions')
|
||||||
|
$for method in App.methods {
|
||||||
|
println('Method: $method.name')
|
||||||
|
println('Attributes: $method.attrs')
|
||||||
|
}
|
||||||
|
$for field in App.fields {
|
||||||
|
$if field.@type is string {
|
||||||
|
println(field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) method_one() {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) method_two() {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) method_three() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) method_four() int {
|
||||||
|
return 1
|
||||||
|
}
|
@ -282,3 +282,21 @@ pub:
|
|||||||
value string
|
value string
|
||||||
method string
|
method string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FunctionData {
|
||||||
|
pub:
|
||||||
|
name string
|
||||||
|
attrs []string
|
||||||
|
ret_type string
|
||||||
|
@type int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FieldData {
|
||||||
|
pub:
|
||||||
|
name string
|
||||||
|
attrs []string
|
||||||
|
typ string
|
||||||
|
is_pub bool
|
||||||
|
is_mut bool
|
||||||
|
@type int
|
||||||
|
}
|
||||||
|
@ -346,9 +346,9 @@ pub struct File {
|
|||||||
pub:
|
pub:
|
||||||
path string
|
path string
|
||||||
mod Module
|
mod Module
|
||||||
scope &Scope
|
|
||||||
global_scope &Scope
|
global_scope &Scope
|
||||||
pub mut:
|
pub mut:
|
||||||
|
scope &Scope
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
imports []Import
|
imports []Import
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
@ -524,23 +524,43 @@ When .is_opt is true, the code should compile, even
|
|||||||
if `xyz` is NOT defined.
|
if `xyz` is NOT defined.
|
||||||
If .is_opt is false, then when `xyz` is not defined,
|
If .is_opt is false, then when `xyz` is not defined,
|
||||||
the compilation will fail.
|
the compilation will fail.
|
||||||
|
|
||||||
|
`$if method.type is string {}` will produce CompIf with:
|
||||||
|
.is_typecheck true,
|
||||||
|
.tchk_expr: method.type
|
||||||
|
.tchk_type: string
|
||||||
|
.tchk_match: true on each iteration, having a string `method.type`
|
||||||
*/
|
*/
|
||||||
|
pub enum CompIfKind {
|
||||||
|
platform
|
||||||
|
typecheck
|
||||||
|
}
|
||||||
pub struct CompIf {
|
pub struct CompIf {
|
||||||
pub:
|
pub:
|
||||||
val string
|
val string
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
is_not bool
|
is_not bool
|
||||||
|
kind CompIfKind
|
||||||
|
tchk_expr Expr
|
||||||
|
tchk_type table.Type
|
||||||
pos token.Position
|
pos token.Position
|
||||||
pub mut:
|
pub mut:
|
||||||
|
tchk_match bool
|
||||||
is_opt bool
|
is_opt bool
|
||||||
has_else bool
|
has_else bool
|
||||||
else_stmts []Stmt
|
else_stmts []Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum CompForKind {
|
||||||
|
methods
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CompFor {
|
pub struct CompFor {
|
||||||
pub:
|
pub:
|
||||||
val_var string
|
val_var string
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
|
kind CompForKind
|
||||||
pub mut:
|
pub mut:
|
||||||
// expr Expr
|
// expr Expr
|
||||||
typ table.Type
|
typ table.Type
|
||||||
|
@ -314,3 +314,10 @@ pub fn (node Stmt) str() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (e CompForKind) str() string {
|
||||||
|
match e {
|
||||||
|
.methods { return 'methods' }
|
||||||
|
.fields { return 'fields' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1913,7 +1913,6 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
|||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
}
|
}
|
||||||
ast.CompIf {
|
ast.CompIf {
|
||||||
// c.expr(node.cond)
|
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
if node.has_else {
|
if node.has_else {
|
||||||
c.stmts(node.else_stmts)
|
c.stmts(node.else_stmts)
|
||||||
|
@ -305,11 +305,27 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
|
|||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.CompFor {}
|
ast.CompFor {
|
||||||
|
typ := f.no_cur_mod(f.table.type_to_str(it.typ))
|
||||||
|
f.writeln('\$for $it.val_var in ${typ}($it.kind.str()) {')
|
||||||
|
f.stmts(it.stmts)
|
||||||
|
f.writeln('}')
|
||||||
|
}
|
||||||
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 { '' }
|
||||||
f.writeln('\$if $inversion$it.val$is_opt {')
|
mut typecheck := ''
|
||||||
|
if it.kind == .typecheck {
|
||||||
|
typ := f.no_cur_mod(f.table.type_to_str(it.tchk_type))
|
||||||
|
typecheck = ' is $typ'
|
||||||
|
f.write('\$if $inversion')
|
||||||
|
f.expr(it.tchk_expr)
|
||||||
|
f.write(is_opt)
|
||||||
|
f.write(typecheck)
|
||||||
|
f.writeln(' {')
|
||||||
|
} else {
|
||||||
|
f.writeln('\$if $inversion$it.val$is_opt {')
|
||||||
|
}
|
||||||
f.stmts(it.stmts)
|
f.stmts(it.stmts)
|
||||||
if it.has_else {
|
if it.has_else {
|
||||||
f.writeln('} \$else {')
|
f.writeln('} \$else {')
|
||||||
|
@ -24,80 +24,81 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
struct Gen {
|
struct Gen {
|
||||||
table &table.Table
|
table &table.Table
|
||||||
pref &pref.Preferences
|
pref &pref.Preferences
|
||||||
module_built string
|
module_built string
|
||||||
mut:
|
mut:
|
||||||
out strings.Builder
|
out strings.Builder
|
||||||
cheaders strings.Builder
|
cheaders strings.Builder
|
||||||
includes strings.Builder // all C #includes required by V modules
|
includes strings.Builder // all C #includes required by V modules
|
||||||
typedefs strings.Builder
|
typedefs strings.Builder
|
||||||
typedefs2 strings.Builder
|
typedefs2 strings.Builder
|
||||||
type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
|
type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
|
||||||
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
|
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
|
||||||
inits map[string]strings.Builder // contents of `void _vinit(){}`
|
inits map[string]strings.Builder // contents of `void _vinit(){}`
|
||||||
cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
|
cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
|
||||||
gowrappers strings.Builder // all go callsite wrappers
|
gowrappers strings.Builder // all go callsite wrappers
|
||||||
stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined
|
stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined
|
||||||
auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs
|
auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs
|
||||||
comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI
|
comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI
|
||||||
pcs_declarations strings.Builder // -prof profile counter declarations for each function
|
pcs_declarations strings.Builder // -prof profile counter declarations for each function
|
||||||
hotcode_definitions strings.Builder // -live declarations & functions
|
hotcode_definitions strings.Builder // -live declarations & functions
|
||||||
options strings.Builder // `Option_xxxx` types
|
options strings.Builder // `Option_xxxx` types
|
||||||
json_forward_decls strings.Builder // json type forward decls
|
json_forward_decls strings.Builder // json type forward decls
|
||||||
enum_typedefs strings.Builder // enum types
|
enum_typedefs strings.Builder // enum types
|
||||||
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
|
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
|
||||||
file ast.File
|
file ast.File
|
||||||
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
||||||
last_fn_c_name string
|
last_fn_c_name string
|
||||||
tmp_count int
|
tmp_count int
|
||||||
variadic_args map[string]int
|
variadic_args map[string]int
|
||||||
is_c_call bool // e.g. `C.printf("v")`
|
is_c_call bool // e.g. `C.printf("v")`
|
||||||
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
|
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
|
||||||
is_assign_rhs bool // inside right part of assign after `=` (val expr)
|
is_assign_rhs bool // inside right part of assign after `=` (val expr)
|
||||||
is_array_set bool
|
is_array_set bool
|
||||||
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
|
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
|
||||||
is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
|
is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
|
||||||
is_shared bool // for initialization of hidden mutex in `[rw]shared` literals
|
is_shared bool // for initialization of hidden mutex in `[rw]shared` literals
|
||||||
optionals []string // to avoid duplicates TODO perf, use map
|
optionals []string // to avoid duplicates TODO perf, use map
|
||||||
shareds []int // types with hidden mutex for which decl has been emitted
|
shareds []int // types with hidden mutex for which decl has been emitted
|
||||||
inside_ternary int // ?: comma separated statements on a single line
|
inside_ternary int // ?: comma separated statements on a single line
|
||||||
inside_map_postfix bool // inside map++/-- postfix expr
|
inside_map_postfix bool // inside map++/-- postfix expr
|
||||||
inside_map_infix bool // inside map<</+=/-= infix expr
|
inside_map_infix bool // inside map<</+=/-= infix expr
|
||||||
ternary_names map[string]string
|
ternary_names map[string]string
|
||||||
ternary_level_names map[string][]string
|
ternary_level_names map[string][]string
|
||||||
stmt_path_pos []int
|
stmt_path_pos []int
|
||||||
right_is_opt bool
|
right_is_opt bool
|
||||||
autofree bool
|
autofree bool
|
||||||
indent int
|
indent int
|
||||||
empty_line bool
|
empty_line bool
|
||||||
is_test bool
|
is_test bool
|
||||||
assign_op token.Kind // *=, =, etc (for array_set)
|
assign_op token.Kind // *=, =, etc (for array_set)
|
||||||
defer_stmts []ast.DeferStmt
|
defer_stmts []ast.DeferStmt
|
||||||
defer_ifdef string
|
defer_ifdef string
|
||||||
defer_profile_code string
|
defer_profile_code string
|
||||||
str_types []string // types that need automatic str() generation
|
str_types []string // types that need automatic str() generation
|
||||||
threaded_fns []string // for generating unique wrapper types and fns for `go xxx()`
|
threaded_fns []string // for generating unique wrapper types and fns for `go xxx()`
|
||||||
array_fn_definitions []string // array equality functions that have been defined
|
array_fn_definitions []string // array equality functions that have been defined
|
||||||
map_fn_definitions []string // map equality functions that have been defined
|
map_fn_definitions []string // map equality functions that have been defined
|
||||||
is_json_fn bool // inside json.encode()
|
is_json_fn bool // inside json.encode()
|
||||||
json_types []string // to avoid json gen duplicates
|
json_types []string // to avoid json gen duplicates
|
||||||
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
|
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
|
||||||
attrs []string // attributes before next decl stmt
|
attrs []string // attributes before next decl stmt
|
||||||
is_builtin_mod bool
|
is_builtin_mod bool
|
||||||
hotcode_fn_names []string
|
hotcode_fn_names []string
|
||||||
// cur_fn ast.FnDecl
|
// cur_fn ast.FnDecl
|
||||||
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
||||||
sql_i int
|
sql_i int
|
||||||
sql_stmt_name string
|
sql_stmt_name string
|
||||||
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
||||||
inside_vweb_tmpl bool
|
inside_vweb_tmpl bool
|
||||||
inside_return bool
|
inside_return bool
|
||||||
strs_to_free string
|
strs_to_free string
|
||||||
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 {
|
comp_for_method string // $for method in T {
|
||||||
|
comptime_var_type_map map[string]table.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -43,7 +43,7 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
|
|||||||
if m.args.len > 1 {
|
if m.args.len > 1 {
|
||||||
g.write(', ')
|
g.write(', ')
|
||||||
}
|
}
|
||||||
for i in 1 .. m.args.len{
|
for i in 1 .. m.args.len {
|
||||||
if node.left is ast.Ident {
|
if node.left is ast.Ident {
|
||||||
left_name := node.left as ast.Ident
|
left_name := node.left as ast.Ident
|
||||||
if m.args[i].name == left_name.name {
|
if m.args[i].name == left_name.name {
|
||||||
@ -88,10 +88,39 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) comp_if(it ast.CompIf) {
|
fn (mut g Gen) comp_if(mut it ast.CompIf) {
|
||||||
if it.stmts.len == 0 && it.else_stmts.len == 0 {
|
if it.stmts.len == 0 && it.else_stmts.len == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if it.kind == .typecheck {
|
||||||
|
mut comptime_var_type := table.Type(0)
|
||||||
|
if it.tchk_expr is ast.SelectorExpr {
|
||||||
|
se := it.tchk_expr as ast.SelectorExpr
|
||||||
|
x := se.expr.str()
|
||||||
|
comptime_var_type = g.comptime_var_type_map[ x ]
|
||||||
|
}
|
||||||
|
if comptime_var_type == 0 {
|
||||||
|
$if trace_gen ? {
|
||||||
|
eprintln('Known compile time types: ')
|
||||||
|
eprintln( g.comptime_var_type_map.str() )
|
||||||
|
}
|
||||||
|
verror('the compile time type of `$it.tchk_expr.str()` is unknown')
|
||||||
|
}
|
||||||
|
ret_type_name := g.table.get_type_symbol( comptime_var_type ).name
|
||||||
|
it_type_name := g.table.get_type_symbol(it.tchk_type).name
|
||||||
|
types_match := comptime_var_type == it.tchk_type
|
||||||
|
g.writeln('{ // \$if ${it.val} is ${it_type_name}, typecheck start, $comptime_var_type == $it.tchk_type => $ret_type_name == $it_type_name => $types_match ')
|
||||||
|
mut stmts := it.stmts
|
||||||
|
if !types_match {
|
||||||
|
stmts = []ast.Stmt{}
|
||||||
|
if it.has_else {
|
||||||
|
stmts = it.else_stmts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.stmts(stmts)
|
||||||
|
g.writeln('} // typecheck end')
|
||||||
|
return
|
||||||
|
}
|
||||||
ifdef := g.comp_if_to_ifdef(it.val, it.is_opt)
|
ifdef := g.comp_if_to_ifdef(it.val, it.is_opt)
|
||||||
g.empty_line = false
|
g.empty_line = false
|
||||||
if it.is_not {
|
if it.is_not {
|
||||||
@ -121,42 +150,83 @@ fn (mut g Gen) comp_if(it ast.CompIf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) comp_for(node ast.CompFor) {
|
fn (mut g Gen) comp_for(node ast.CompFor) {
|
||||||
g.writeln('// 2comptime $' + 'for {')
|
|
||||||
sym := g.table.get_type_symbol(g.unwrap_generic(node.typ))
|
sym := g.table.get_type_symbol(g.unwrap_generic(node.typ))
|
||||||
vweb_result_type := table.new_type(g.table.find_type_idx('vweb.Result'))
|
g.writeln('{ // 2comptime: \$for $node.val_var in ${sym.name}(${node.kind.str()}) {')
|
||||||
|
// vweb_result_type := table.new_type(g.table.find_type_idx('vweb.Result'))
|
||||||
mut i := 0
|
mut i := 0
|
||||||
// g.writeln('string method = tos_lit("");')
|
// g.writeln('string method = tos_lit("");')
|
||||||
mut methods := sym.methods.filter(it.attrs.len == 0) // methods without attrs first
|
if node.kind == .methods {
|
||||||
methods_with_attrs := sym.methods.filter(it.attrs.len > 0) // methods without attrs first
|
mut methods := sym.methods.filter(it.attrs.len == 0) // methods without attrs first
|
||||||
methods << methods_with_attrs
|
methods_with_attrs := sym.methods.filter(it.attrs.len > 0) // methods without attrs first
|
||||||
for method in methods { // sym.methods {
|
methods << methods_with_attrs
|
||||||
// if method.attrs.len == 0 {
|
if methods.len > 0 {
|
||||||
// continue
|
g.writeln('\tFunctionData $node.val_var;')
|
||||||
// }
|
g.writeln('\tmemset(&${node.val_var}, 0, sizeof(FunctionData));')
|
||||||
if method.return_type != vweb_result_type { // table.void_type {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
g.comp_for_method = method.name
|
for method in methods { // sym.methods {
|
||||||
g.writeln('\t// method $i')
|
/*
|
||||||
if i == 0 {
|
if method.return_type != vweb_result_type { // table.void_type {
|
||||||
g.write('\tstring ')
|
continue
|
||||||
}
|
|
||||||
g.writeln('method = tos_lit("$method.name");')
|
|
||||||
if i == 0 {
|
|
||||||
g.write('\tarray_string ')
|
|
||||||
}
|
|
||||||
if method.attrs.len == 0 {
|
|
||||||
g.writeln('attrs = new_array_from_c_array(0, 0, sizeof(string), _MOV((string[0]){}));')
|
|
||||||
} else {
|
|
||||||
mut attrs := []string{}
|
|
||||||
for attrib in method.attrs {
|
|
||||||
attrs << 'tos_lit("$attrib")'
|
|
||||||
}
|
}
|
||||||
g.writeln('attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' + attrs.join(', ') + '}));')
|
*/
|
||||||
|
g.comp_for_method = method.name
|
||||||
|
g.writeln('\t// method $i')
|
||||||
|
g.writeln('\t${node.val_var}.name = tos_lit("$method.name");')
|
||||||
|
if method.attrs.len == 0 {
|
||||||
|
g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
|
||||||
|
} else {
|
||||||
|
mut attrs := []string{}
|
||||||
|
for attrib in method.attrs {
|
||||||
|
attrs << 'tos_lit("$attrib")'
|
||||||
|
}
|
||||||
|
g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
|
||||||
|
attrs.join(', ') + '}));')
|
||||||
|
}
|
||||||
|
method_sym := g.table.get_type_symbol(method.return_type)
|
||||||
|
g.writeln('\t${node.val_var}.ret_type = tos_lit("${method_sym.name}");')
|
||||||
|
g.writeln('\t${node.val_var}.type = ${int(method.return_type).str()};')
|
||||||
|
//
|
||||||
|
g.comptime_var_type_map[ node.val_var ] = method.return_type
|
||||||
|
g.stmts(node.stmts)
|
||||||
|
i++
|
||||||
|
g.writeln('')
|
||||||
|
}
|
||||||
|
g.comptime_var_type_map.delete( node.val_var )
|
||||||
|
} else if node.kind == .fields {
|
||||||
|
// TODO add fields
|
||||||
|
if sym.info is table.Struct {
|
||||||
|
info := sym.info as table.Struct
|
||||||
|
mut fields := info.fields.filter(it.attrs.len == 0)
|
||||||
|
fields_with_attrs := info.fields.filter(it.attrs.len > 0)
|
||||||
|
fields << fields_with_attrs
|
||||||
|
if fields.len > 0 {
|
||||||
|
g.writeln('\tFieldData $node.val_var;')
|
||||||
|
g.writeln('\tmemset(&${node.val_var}, 0, sizeof(FieldData));')
|
||||||
|
}
|
||||||
|
for field in fields {
|
||||||
|
g.writeln('\t// field $i')
|
||||||
|
g.writeln('\t${node.val_var}.name = tos_lit("$field.name");')
|
||||||
|
if field.attrs.len == 0 {
|
||||||
|
g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
|
||||||
|
} else {
|
||||||
|
mut attrs := []string{}
|
||||||
|
for attrib in field.attrs {
|
||||||
|
attrs << 'tos_lit("$attrib")'
|
||||||
|
}
|
||||||
|
g.writeln('\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' + attrs.join(', ') + '}));')
|
||||||
|
}
|
||||||
|
field_sym := g.table.get_type_symbol( field.typ )
|
||||||
|
g.writeln('\t${node.val_var}.typ = tos_lit("$field_sym.name");')
|
||||||
|
g.writeln('\t${node.val_var}.type = ${int(field.typ).str()};')
|
||||||
|
g.writeln('\t${node.val_var}.is_pub = $field.is_pub;')
|
||||||
|
g.writeln('\t${node.val_var}.is_mut = $field.is_mut;')
|
||||||
|
g.comptime_var_type_map[ node.val_var ] = field.typ
|
||||||
|
g.stmts(node.stmts)
|
||||||
|
i++
|
||||||
|
g.writeln('')
|
||||||
|
}
|
||||||
|
g.comptime_var_type_map.delete( node.val_var )
|
||||||
}
|
}
|
||||||
g.stmts(node.stmts)
|
|
||||||
i++
|
|
||||||
g.writeln('')
|
|
||||||
}
|
}
|
||||||
g.writeln('// } comptime for')
|
g.writeln('} // } comptime for')
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,7 @@ fn (mut p Parser) resolve_vroot(flag string) string {
|
|||||||
vmod_file_location := mcache.get_by_folder(p.file_name_dir)
|
vmod_file_location := mcache.get_by_folder(p.file_name_dir)
|
||||||
if vmod_file_location.vmod_file.len == 0 {
|
if vmod_file_location.vmod_file.len == 0 {
|
||||||
// There was no actual v.mod file found.
|
// There was no actual v.mod file found.
|
||||||
p.error('To use @VROOT, you need' + ' to have a "v.mod" file in $p.file_name_dir,' +
|
p.error('To use @VROOT, you need' + ' to have a "v.mod" file in $p.file_name_dir,' + ' or in one of its parent folders.')
|
||||||
' or in one of its parent folders.')
|
|
||||||
}
|
}
|
||||||
vmod_path := vmod_file_location.vmod_folder
|
vmod_path := vmod_file_location.vmod_folder
|
||||||
if p.pref.is_fmt {
|
if p.pref.is_fmt {
|
||||||
@ -91,11 +90,8 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
|
|||||||
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'
|
html_name := '${fn_path.last()}.html'
|
||||||
|
|
||||||
|
|
||||||
// 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('/'))
|
||||||
@ -155,26 +151,37 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) comp_for() ast.CompFor {
|
fn (mut p Parser) comp_for() ast.CompFor {
|
||||||
|
// p.comp_for() handles these special forms:
|
||||||
|
// $for method in App(methods) {
|
||||||
|
// $for field in App(fields) {
|
||||||
p.next()
|
p.next()
|
||||||
p.check(.key_for)
|
p.check(.key_for)
|
||||||
val_var := p.check_name()
|
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: p.table.find_type_idx('array_string')
|
|
||||||
})
|
|
||||||
p.check(.key_in)
|
p.check(.key_in)
|
||||||
// expr := p.expr(0)
|
lang := p.parse_language()
|
||||||
typ := p.parse_type()
|
typ := p.parse_any_type(lang, false, false)
|
||||||
// p.check(.dot)
|
p.check(.dot)
|
||||||
// p.check_name()
|
for_val := p.check_name()
|
||||||
|
mut kind := ast.CompForKind.methods
|
||||||
|
if for_val == 'methods' {
|
||||||
|
p.scope.register(val_var, ast.Var{
|
||||||
|
name: val_var
|
||||||
|
typ: p.table.find_type_idx('FunctionData')
|
||||||
|
})
|
||||||
|
} else if for_val == 'fields' {
|
||||||
|
p.scope.register(val_var, ast.Var{
|
||||||
|
name: val_var
|
||||||
|
typ: p.table.find_type_idx('FieldData')
|
||||||
|
})
|
||||||
|
kind = .fields
|
||||||
|
} else {
|
||||||
|
p.error('unknown kind `$for_val`, available are: `methods` or `fields`')
|
||||||
|
}
|
||||||
stmts := p.parse_block()
|
stmts := p.parse_block()
|
||||||
return ast.CompFor{
|
return ast.CompFor{
|
||||||
val_var: val_var
|
val_var: val_var
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
|
kind: kind
|
||||||
typ: typ
|
typ: typ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,7 +197,33 @@ fn (mut p Parser) comp_if() ast.Stmt {
|
|||||||
if is_not {
|
if is_not {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
val := p.check_name()
|
//
|
||||||
|
name_pos_start := p.tok.position()
|
||||||
|
mut val := ''
|
||||||
|
mut tchk_expr := ast.Expr{}
|
||||||
|
if p.peek_tok.kind == .dot {
|
||||||
|
vname := p.parse_ident(table.Language.v)
|
||||||
|
cobj := p.scope.find(vname.name) or {
|
||||||
|
p.error_with_pos('unknown variable `$vname.name`', name_pos_start)
|
||||||
|
return ast.Stmt{}
|
||||||
|
}
|
||||||
|
if cobj is ast.Var {
|
||||||
|
tchk_expr = p.dot_expr(vname)
|
||||||
|
val = vname.name
|
||||||
|
if tchk_expr is ast.SelectorExpr {
|
||||||
|
if tchk_expr.field_name !in ['type', '@type'] {
|
||||||
|
p.error_with_pos('only the `.@type` field name is supported for now',
|
||||||
|
name_pos_start)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.error_with_pos('`$vname.name` is not a variable', name_pos_start)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val = p.check_name()
|
||||||
|
}
|
||||||
|
name_pos := name_pos_start.extend(p.tok.position())
|
||||||
|
//
|
||||||
mut stmts := []ast.Stmt{}
|
mut stmts := []ast.Stmt{}
|
||||||
mut skip := false
|
mut skip := false
|
||||||
if val in supported_platforms {
|
if val in supported_platforms {
|
||||||
@ -235,18 +268,36 @@ fn (mut p Parser) comp_if() ast.Stmt {
|
|||||||
skip = false
|
skip = false
|
||||||
}
|
}
|
||||||
mut is_opt := false
|
mut is_opt := false
|
||||||
|
mut is_typecheck := false
|
||||||
|
mut tchk_type := table.Type(0)
|
||||||
if p.tok.kind == .question {
|
if p.tok.kind == .question {
|
||||||
p.next()
|
p.next()
|
||||||
is_opt = true
|
is_opt = true
|
||||||
|
} else if p.tok.kind == .key_is {
|
||||||
|
p.next()
|
||||||
|
tchk_type = p.parse_type()
|
||||||
|
is_typecheck = true
|
||||||
}
|
}
|
||||||
if !skip {
|
if !skip {
|
||||||
stmts = p.parse_block()
|
stmts = p.parse_block()
|
||||||
}
|
}
|
||||||
|
if !is_typecheck && val.len == 0 {
|
||||||
|
p.error_with_pos('Only `\$if compvarname.field is type {}` is supported', name_pos)
|
||||||
|
}
|
||||||
|
if is_typecheck {
|
||||||
|
match tchk_expr {
|
||||||
|
ast.SelectorExpr {}
|
||||||
|
else { p.error_with_pos('Only compvarname.field is supported', name_pos) }
|
||||||
|
}
|
||||||
|
}
|
||||||
mut node := ast.CompIf{
|
mut node := ast.CompIf{
|
||||||
is_not: is_not
|
is_not: is_not
|
||||||
is_opt: is_opt
|
is_opt: is_opt
|
||||||
|
kind: if is_typecheck { ast.CompIfKind.typecheck } else { ast.CompIfKind.platform }
|
||||||
pos: pos
|
pos: pos
|
||||||
val: val
|
val: val
|
||||||
|
tchk_type: tchk_type
|
||||||
|
tchk_expr: tchk_expr
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
}
|
}
|
||||||
if p.tok.kind == .dollar && p.peek_tok.kind == .key_else {
|
if p.tok.kind == .dollar && p.peek_tok.kind == .key_else {
|
||||||
|
@ -143,7 +143,7 @@ pub fn (mut p Parser) parse_type() table.Type {
|
|||||||
mut typ := table.void_type
|
mut typ := table.void_type
|
||||||
if p.tok.kind != .lcbr {
|
if p.tok.kind != .lcbr {
|
||||||
pos := p.tok.position()
|
pos := p.tok.position()
|
||||||
typ = p.parse_any_type(language, nr_muls > 0)
|
typ = p.parse_any_type(language, nr_muls > 0, true)
|
||||||
if typ == table.void_type {
|
if typ == table.void_type {
|
||||||
p.error_with_pos('use `?` instead of `?void`', pos)
|
p.error_with_pos('use `?` instead of `?void`', pos)
|
||||||
}
|
}
|
||||||
@ -163,13 +163,13 @@ pub fn (mut p Parser) parse_type() table.Type {
|
|||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool) table.Type {
|
pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr, check_dot bool) table.Type {
|
||||||
mut name := p.tok.lit
|
mut name := p.tok.lit
|
||||||
if language == .c {
|
if language == .c {
|
||||||
name = 'C.$name'
|
name = 'C.$name'
|
||||||
} else if language == .js {
|
} else if language == .js {
|
||||||
name = 'JS.$name'
|
name = 'JS.$name'
|
||||||
} else if p.peek_tok.kind == .dot {
|
} else if p.peek_tok.kind == .dot && check_dot {
|
||||||
// `module.Type`
|
// `module.Type`
|
||||||
// /if !(p.tok.lit in p.table.imports) {
|
// /if !(p.tok.lit in p.table.imports) {
|
||||||
if !p.known_import(name) {
|
if !p.known_import(name) {
|
||||||
|
@ -1,4 +1,15 @@
|
|||||||
struct App {
|
struct App {
|
||||||
|
a string
|
||||||
|
b string
|
||||||
|
mut:
|
||||||
|
c int
|
||||||
|
d f32
|
||||||
|
pub:
|
||||||
|
e f32
|
||||||
|
f u64
|
||||||
|
pub mut:
|
||||||
|
g string
|
||||||
|
h byte
|
||||||
}
|
}
|
||||||
|
|
||||||
['foo/bar/three']
|
['foo/bar/three']
|
||||||
@ -9,19 +20,55 @@ fn (mut app App) run() {
|
|||||||
fn (mut app App) method2() {
|
fn (mut app App) method2() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_comptime_for() {
|
fn (mut app App) int_method1() int {
|
||||||
/*
|
return 0
|
||||||
app := App{}
|
}
|
||||||
|
|
||||||
$for method in App { //.method_attrs {
|
fn (mut app App) int_method2() int {
|
||||||
words := attrs.split('/')
|
return 1
|
||||||
println(words)
|
}
|
||||||
//println(method.value)
|
|
||||||
}
|
fn no_lines(s string) string { return s.replace('\n', ' ') }
|
||||||
assert true
|
|
||||||
println('DONE')
|
fn test_comptime_for() {
|
||||||
*/
|
println(@FN)
|
||||||
if true {}
|
methods := ['run', 'method2', 'int_method1', 'int_method2']
|
||||||
//
|
$for method in App.methods {
|
||||||
else{}
|
println(' method: $method.name | ' + no_lines('$method'))
|
||||||
|
assert method.name in methods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_comptime_for_with_if() {
|
||||||
|
println(@FN)
|
||||||
|
methods := ['int_method1', 'int_method2']
|
||||||
|
$for method in App.methods {
|
||||||
|
println(' method: ' + no_lines('$method'))
|
||||||
|
$if method.@type is int {
|
||||||
|
println(method.attrs)
|
||||||
|
assert method.name in methods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_comptime_for_fields() {
|
||||||
|
println(@FN)
|
||||||
|
$for field in App.fields {
|
||||||
|
println(' field: $field.name | ' + no_lines('$field'))
|
||||||
|
$if field.@type is string {
|
||||||
|
assert field.name in ['a', 'b', 'g']
|
||||||
|
}
|
||||||
|
$if field.@type is f32 {
|
||||||
|
assert field.name in ['d', 'e']
|
||||||
|
}
|
||||||
|
if field.is_mut {
|
||||||
|
assert field.name in ['c', 'd', 'g', 'h']
|
||||||
|
}
|
||||||
|
if field.is_pub {
|
||||||
|
assert field.name in ['e', 'f', 'g', 'h']
|
||||||
|
}
|
||||||
|
if field.is_pub && field.is_mut {
|
||||||
|
assert field.name in ['g', 'h']
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
177
vlib/vweb/vweb.v
177
vlib/vweb/vweb.v
@ -362,91 +362,94 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
|
|||||||
|
|
||||||
mut vars := []string{cap: route_words_a.len}
|
mut vars := []string{cap: route_words_a.len}
|
||||||
mut action := ''
|
mut action := ''
|
||||||
$for method in T {
|
$for method in T.methods {
|
||||||
route_words_a = [][]string{}
|
$if method.@type is Result {
|
||||||
if attrs.len == 0 {
|
attrs := method.attrs
|
||||||
// No routing for this method. If it matches, call it and finish matching
|
route_words_a = [][]string{}
|
||||||
// since such methods have a priority.
|
if attrs.len == 0 {
|
||||||
// For example URL `/register` matches route `/:user`, but `fn register()`
|
// No routing for this method. If it matches, call it and finish matching
|
||||||
// should be called first.
|
// since such methods have a priority.
|
||||||
if (req.method == 'GET' && url_words[0] == method && url_words.len == 1) || (req.method == 'POST' && url_words[0] + '_post' == method) {
|
// For example URL `/register` matches route `/:user`, but `fn register()`
|
||||||
println('easy match method=$method')
|
// should be called first.
|
||||||
app.$method(vars)
|
if (req.method == 'GET' && url_words[0] == method.name && url_words.len == 1) || (req.method == 'POST' && url_words[0] + '_post' == method.name) {
|
||||||
return
|
println('easy match method=$method.name')
|
||||||
}
|
app.$method(vars)
|
||||||
} else {
|
return
|
||||||
// Get methods
|
|
||||||
// Get is default
|
|
||||||
if 'post' in attrs {
|
|
||||||
if req.method == 'POST' {
|
|
||||||
route_words_a = attrs.filter(it.to_lower() != 'post').map(it[1..].split('/'))
|
|
||||||
}
|
|
||||||
} else if 'put' in attrs {
|
|
||||||
if req.method == 'PUT' {
|
|
||||||
route_words_a = attrs.filter(it.to_lower() != 'put').map(it[1..].split('/'))
|
|
||||||
}
|
|
||||||
} else if 'patch' in attrs {
|
|
||||||
if req.method == 'PATCH' {
|
|
||||||
route_words_a = attrs.filter(it.to_lower() != 'patch').map(it[1..].split('/'))
|
|
||||||
}
|
|
||||||
} else if 'delete' in attrs {
|
|
||||||
if req.method == 'DELETE' {
|
|
||||||
route_words_a = attrs.filter(it.to_lower() != 'delete').map(it[1..].split('/'))
|
|
||||||
}
|
|
||||||
} else if 'head' in attrs {
|
|
||||||
if req.method == 'HEAD' {
|
|
||||||
route_words_a = attrs.filter(it.to_lower() != 'head').map(it[1..].split('/'))
|
|
||||||
}
|
|
||||||
} else if 'options' in attrs {
|
|
||||||
if req.method == 'OPTIONS' {
|
|
||||||
route_words_a = attrs.filter(it.to_lower() != 'options').map(it[1..].split('/'))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
route_words_a = attrs.filter(it.to_lower() != 'get').map(it[1..].split('/'))
|
// Get methods
|
||||||
}
|
// Get is default
|
||||||
if route_words_a.len > 0 {
|
if 'post' in attrs {
|
||||||
for route_words in route_words_a {
|
if req.method == 'POST' {
|
||||||
if url_words.len == route_words.len || (url_words.len >= route_words.len - 1 && route_words.last().ends_with('...')) {
|
route_words_a = attrs.filter(it.to_lower() != 'post').map(it[1..].split('/'))
|
||||||
// match `/:user/:repo/tree` to `/vlang/v/tree`
|
}
|
||||||
mut matching := false
|
} else if 'put' in attrs {
|
||||||
mut unknown := false
|
if req.method == 'PUT' {
|
||||||
mut variables := []string{cap: route_words.len}
|
route_words_a = attrs.filter(it.to_lower() != 'put').map(it[1..].split('/'))
|
||||||
for i in 0..route_words.len {
|
}
|
||||||
if url_words.len == i {
|
} else if 'patch' in attrs {
|
||||||
variables << ''
|
if req.method == 'PATCH' {
|
||||||
matching = true
|
route_words_a = attrs.filter(it.to_lower() != 'patch').map(it[1..].split('/'))
|
||||||
unknown = true
|
}
|
||||||
break
|
} else if 'delete' in attrs {
|
||||||
}
|
if req.method == 'DELETE' {
|
||||||
if url_words[i] == route_words[i] {
|
route_words_a = attrs.filter(it.to_lower() != 'delete').map(it[1..].split('/'))
|
||||||
// no parameter
|
}
|
||||||
matching = true
|
} else if 'head' in attrs {
|
||||||
continue
|
if req.method == 'HEAD' {
|
||||||
} else if route_words[i].starts_with(':') {
|
route_words_a = attrs.filter(it.to_lower() != 'head').map(it[1..].split('/'))
|
||||||
// is parameter
|
}
|
||||||
if i < route_words.len && !route_words[i].ends_with('...') {
|
} else if 'options' in attrs {
|
||||||
// normal parameter
|
if req.method == 'OPTIONS' {
|
||||||
variables << url_words[i]
|
route_words_a = attrs.filter(it.to_lower() != 'options').map(it[1..].split('/'))
|
||||||
} else {
|
}
|
||||||
// array parameter only in the end
|
} else {
|
||||||
variables << url_words[i..].join('/')
|
route_words_a = attrs.filter(it.to_lower() != 'get').map(it[1..].split('/'))
|
||||||
|
}
|
||||||
|
if route_words_a.len > 0 {
|
||||||
|
for route_words in route_words_a {
|
||||||
|
if url_words.len == route_words.len || (url_words.len >= route_words.len - 1 && route_words.last().ends_with('...')) {
|
||||||
|
// match `/:user/:repo/tree` to `/vlang/v/tree`
|
||||||
|
mut matching := false
|
||||||
|
mut unknown := false
|
||||||
|
mut variables := []string{cap: route_words.len}
|
||||||
|
for i in 0..route_words.len {
|
||||||
|
if url_words.len == i {
|
||||||
|
variables << ''
|
||||||
|
matching = true
|
||||||
|
unknown = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if url_words[i] == route_words[i] {
|
||||||
|
// no parameter
|
||||||
|
matching = true
|
||||||
|
continue
|
||||||
|
} else if route_words[i].starts_with(':') {
|
||||||
|
// is parameter
|
||||||
|
if i < route_words.len && !route_words[i].ends_with('...') {
|
||||||
|
// normal parameter
|
||||||
|
variables << url_words[i]
|
||||||
|
} else {
|
||||||
|
// array parameter only in the end
|
||||||
|
variables << url_words[i..].join('/')
|
||||||
|
}
|
||||||
|
matching = true
|
||||||
|
unknown = true
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
matching = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
matching = true
|
|
||||||
unknown = true
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
matching = false
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
if matching && !unknown {
|
||||||
if matching && !unknown {
|
// absolute router words like `/test/site`
|
||||||
// absolute router words like `/test/site`
|
app.$method(vars)
|
||||||
app.$method(vars)
|
return
|
||||||
return
|
} else if matching && unknown {
|
||||||
} else if matching && unknown {
|
// router words with paramter like `/:test/site`
|
||||||
// router words with paramter like `/:test/site`
|
action = method.name
|
||||||
action = method
|
vars = variables
|
||||||
vars = variables
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,11 +466,13 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
|
|||||||
|
|
||||||
fn send_action<T>(action string, vars []string, mut app T) {
|
fn send_action<T>(action string, vars []string, mut app T) {
|
||||||
// TODO remove this function
|
// TODO remove this function
|
||||||
$for method in T {
|
$for method in T.methods {
|
||||||
// search again for method
|
$if method.@type is Result {
|
||||||
if action == method && attrs.len > 0 {
|
// search again for method
|
||||||
// call action method
|
if action == method.name && method.attrs.len > 0 {
|
||||||
app.$method(vars)
|
// call action method
|
||||||
|
app.$method(vars)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user