mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
generics, vweb, comptime codegen, etc
This commit is contained in:
152
compiler/fn.v
152
compiler/fn.v
@@ -30,6 +30,7 @@ mut:
|
||||
returns_error bool
|
||||
is_decl bool // type myfn fn(int, int)
|
||||
defer_text string
|
||||
//gen_types []string
|
||||
}
|
||||
|
||||
fn (f &Fn) find_var(name string) Var {
|
||||
@@ -110,7 +111,7 @@ fn (p mut Parser) fn_decl() {
|
||||
defer { p.fgenln('\n') }
|
||||
is_pub := p.tok == .key_pub
|
||||
is_live := p.attr == 'live' && !p.pref.is_so && p.pref.is_live
|
||||
if p.attr == 'live' && p.first_run() && !p.pref.is_live && !p.pref.is_so {
|
||||
if p.attr == 'live' && p.first_pass() && !p.pref.is_live && !p.pref.is_so {
|
||||
println('INFO: run `v -live program.v` if you want to use [live] functions')
|
||||
}
|
||||
if is_pub {
|
||||
@@ -136,7 +137,7 @@ fn (p mut Parser) fn_decl() {
|
||||
p.error('invalid receiver type `$receiver_typ` (`$receiver_typ` is an interface)')
|
||||
}
|
||||
// Don't allow modifying types from a different module
|
||||
if !p.first_run() && !p.builtin_pkg && T.mod != p.mod {
|
||||
if !p.first_pass() && !p.builtin_pkg && T.mod != p.mod {
|
||||
println('T.mod=$T.mod')
|
||||
println('p.mod=$p.mod')
|
||||
p.error('cannot define new methods on non-local type `$receiver_typ`')
|
||||
@@ -203,7 +204,7 @@ fn (p mut Parser) fn_decl() {
|
||||
if !is_c && !p.builtin_pkg && p.mod != 'main' && receiver_typ.len == 0 {
|
||||
f.name = p.prepend_pkg(f.name)
|
||||
}
|
||||
if p.first_run() && p.table.known_fn(f.name) && receiver_typ.len == 0 {
|
||||
if p.first_pass() && p.table.known_fn(f.name) && receiver_typ.len == 0 {
|
||||
existing_fn := p.table.find_fn(f.name)
|
||||
// This existing function could be defined as C decl before (no body), then we don't need to throw an erro
|
||||
if !existing_fn.is_decl {
|
||||
@@ -213,13 +214,19 @@ fn (p mut Parser) fn_decl() {
|
||||
// Generic?
|
||||
mut is_generic := false
|
||||
if p.tok == .lt {
|
||||
is_generic = true
|
||||
p.next()
|
||||
gen_type := p.check_name()
|
||||
if gen_type != 'T' {
|
||||
p.error('only `T` is allowed as a generic type for now')
|
||||
}
|
||||
p.check(.gt)
|
||||
is_generic = true
|
||||
if p.first_pass() {
|
||||
p.table.register_generic_fn(f.name)
|
||||
} else {
|
||||
//gen_types := p.table.fn_gen_types(f.name)
|
||||
//println(gen_types)
|
||||
}
|
||||
}
|
||||
// Args (...)
|
||||
p.fn_args(mut f)
|
||||
@@ -247,14 +254,13 @@ fn (p mut Parser) fn_decl() {
|
||||
p.fgen(' ')
|
||||
p.check(.lcbr)
|
||||
}
|
||||
// Register option ? type
|
||||
// Register ?option type
|
||||
if typ.starts_with('Option_') {
|
||||
p.cgen.typedefs << 'typedef Option $typ;'
|
||||
}
|
||||
// Register function
|
||||
f.typ = typ
|
||||
mut str_args := f.str_args(p.table)
|
||||
// println('FN .decL $f.name typ=$f.typ str_args="$str_args"')
|
||||
// Special case for main() args
|
||||
if f.name == 'main' && !has_receiver {
|
||||
if str_args != '' || typ != 'void' {
|
||||
@@ -263,21 +269,19 @@ fn (p mut Parser) fn_decl() {
|
||||
typ = 'int'
|
||||
str_args = 'int argc, char** argv'
|
||||
}
|
||||
|
||||
mut dll_export_linkage := ''
|
||||
|
||||
if p.os == .msvc && p.attr == 'live' && p.pref.is_so {
|
||||
dll_export_linkage = '__declspec(dllexport) '
|
||||
}
|
||||
|
||||
// Only in C code generate User_register() instead of register()
|
||||
dll_export_linkage := if p.os == .msvc && p.attr == 'live' && p.pref.is_so {
|
||||
'__declspec(dllexport) '
|
||||
} else {
|
||||
''
|
||||
}
|
||||
// if p.file_name != '.vwebtmpl.v' {
|
||||
// oif !p.name.ends_with('_vwebview') {
|
||||
if !p.is_vweb {
|
||||
//println('SETTING CUR FN TO "$f.name" "$p.file_name"')
|
||||
p.cur_fn = f
|
||||
}
|
||||
// Generate `User_register()` instead of `register()`
|
||||
// Internally it's still stored as "register" in type User
|
||||
// mut fn_name_cgen := f.name
|
||||
// if receiver_typ != '' {
|
||||
// fn_name_cgen = '${receiver_typ}_$f.name'
|
||||
// fn_name_cgen = fn_name_cgen.replace(' ', '')
|
||||
// fn_name_cgen = fn_name_cgen.replace('*', '')
|
||||
// }
|
||||
mut fn_name_cgen := p.table.cgen_name(f)
|
||||
// Start generation of the function body
|
||||
skip_main_in_test := f.name == 'main' && p.pref.is_test
|
||||
@@ -285,7 +289,27 @@ fn (p mut Parser) fn_decl() {
|
||||
if p.pref.obfuscate {
|
||||
p.genln('; // $f.name')
|
||||
}
|
||||
p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {')
|
||||
// Generate this function's body for all generic types
|
||||
if is_generic {
|
||||
gen_types := p.table.fn_gen_types(f.name)
|
||||
// Remember current scanner position, go back here for each type
|
||||
// TODO remove this once tokens are cached in `new_parser()`
|
||||
cur_pos := p.scanner.pos
|
||||
cur_tok := p.tok
|
||||
cur_lit := p.lit
|
||||
for gen_type in gen_types {
|
||||
p.genln('$dll_export_linkage$typ ${fn_name_cgen}_$gen_type($str_args) {')
|
||||
p.genln('// T start $p.pass ${p.strtok()}')
|
||||
p.cur_gen_type = gen_type // TODO support more than T
|
||||
p.statements()
|
||||
p.scanner.pos = cur_pos
|
||||
p.tok = cur_tok
|
||||
p.lit = cur_lit
|
||||
}
|
||||
}
|
||||
else {
|
||||
p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {')
|
||||
}
|
||||
}
|
||||
if is_fn_header {
|
||||
p.genln('$typ $fn_name_cgen($str_args);')
|
||||
@@ -294,13 +318,12 @@ fn (p mut Parser) fn_decl() {
|
||||
if is_c {
|
||||
p.fgenln('\n')
|
||||
}
|
||||
p.cur_fn = f
|
||||
// Register the method
|
||||
if receiver_typ != '' {
|
||||
mut receiver_t := p.table.find_type(receiver_typ)
|
||||
// No such type yet? It could be defined later. Create a new type.
|
||||
// struct declaration later will modify it instead of creating a new one.
|
||||
if p.first_run() && receiver_t.name == '' {
|
||||
if p.first_pass() && receiver_t.name == '' {
|
||||
// println('fn decl !!!!!!! REG PH $receiver_typ')
|
||||
p.table.register_type2(Type {
|
||||
name: receiver_typ.replace('*', '')
|
||||
@@ -315,8 +338,9 @@ fn (p mut Parser) fn_decl() {
|
||||
// println('register_fn typ=$typ isg=$is_generic')
|
||||
p.table.register_fn(f)
|
||||
}
|
||||
if is_sig || p.first_run() || is_live || is_fn_header || skip_main_in_test {
|
||||
// First pass? Skip the body for now [BIG]
|
||||
if is_sig || p.first_pass() || is_live || is_fn_header || skip_main_in_test {
|
||||
// First pass? Skip the body for now
|
||||
// Look for generic calls.
|
||||
if !is_sig && !is_fn_header {
|
||||
mut opened_scopes := 0
|
||||
mut closed_scopes := 0
|
||||
@@ -328,10 +352,24 @@ fn (p mut Parser) fn_decl() {
|
||||
closed_scopes++
|
||||
}
|
||||
p.next()
|
||||
// find `foo<Bar>()` in function bodies and register generic types
|
||||
// TODO remove this once tokens are cached
|
||||
if p.tok == .gt && p.prev_tok == .name && p.prev_tok2 == .lt &&
|
||||
p.scanner.text[p.scanner.pos-1] != `T` {
|
||||
p.scanner.pos -= 3
|
||||
for p.scanner.pos > 0 && is_name_char(p.scanner.text[p.scanner.pos]) || p.scanner.text[p.scanner.pos] == `.` ||
|
||||
p.scanner.text[p.scanner.pos] == `<` {
|
||||
p.scanner.pos--
|
||||
}
|
||||
p.scanner.pos--
|
||||
p.next()
|
||||
// Run the function in the firt pass to register the generic type
|
||||
p.name_expr()
|
||||
}
|
||||
if p.tok.is_decl() && !(p.prev_tok == .dot && p.tok == .key_type) {
|
||||
break
|
||||
}
|
||||
//fn body ended, and a new fn attribute declaration like [live] is starting?
|
||||
// fn body ended, and a new fn attribute declaration like [live] is starting?
|
||||
if closed_scopes > opened_scopes && p.prev_tok == .rcbr {
|
||||
if p.tok == .lsbr {
|
||||
break
|
||||
@@ -340,18 +378,18 @@ fn (p mut Parser) fn_decl() {
|
||||
}
|
||||
}
|
||||
// Live code reloading? Load all fns from .so
|
||||
if is_live && p.first_run() && p.mod == 'main' {
|
||||
if is_live && p.first_pass() && p.mod == 'main' {
|
||||
//println('ADDING SO FN $fn_name_cgen')
|
||||
p.cgen.so_fns << fn_name_cgen
|
||||
fn_name_cgen = '(* $fn_name_cgen )'
|
||||
}
|
||||
// Actual fn declaration!
|
||||
// Function definition that goes to the top of the C file.
|
||||
mut fn_decl := '$dll_export_linkage$typ $fn_name_cgen($str_args)'
|
||||
if p.pref.obfuscate {
|
||||
fn_decl += '; // ${f.name}'
|
||||
fn_decl += '; // $f.name'
|
||||
}
|
||||
// Add function definition to the top
|
||||
if !is_c && f.name != 'main' && p.first_run() {
|
||||
if !is_c && f.name != 'main' && p.first_pass() {
|
||||
// TODO hack to make Volt compile without -embed_vlib
|
||||
if f.name == 'darwin__nsstring' && p.pref.build_mode == .default_mode {
|
||||
return
|
||||
@@ -360,12 +398,10 @@ fn (p mut Parser) fn_decl() {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if p.attr == 'live' && p.pref.is_so {
|
||||
//p.genln('// live_function body start')
|
||||
p.genln('pthread_mutex_lock(&live_fn_mutex);')
|
||||
}
|
||||
|
||||
if f.name == 'main' || f.name == 'WinMain' {
|
||||
p.genln('init_consts();')
|
||||
if p.table.imports.contains('os') {
|
||||
@@ -404,13 +440,18 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
||||
// println('IS SIG .key_returnING tok=${p.strtok()}')
|
||||
return
|
||||
}
|
||||
// We are in profile mode? Start counting at the beginning of the function (save current time).
|
||||
// Profiling mode? Start counting at the beginning of the function (save current time).
|
||||
if p.pref.is_prof && f.name != 'main' && f.name != 'time__ticks' {
|
||||
p.genln('double _PROF_START = time__ticks();//$f.name')
|
||||
cgen_name := p.table.cgen_name(f)
|
||||
f.defer_text = ' ${cgen_name}_time += time__ticks() - _PROF_START;'
|
||||
}
|
||||
if is_generic {
|
||||
// Don't need to generate body for the actual generic definition
|
||||
p.cgen.nogen = true
|
||||
}
|
||||
p.statements_no_rcbr()
|
||||
p.cgen.nogen = false
|
||||
// Print counting result after all statements in main
|
||||
if p.pref.is_prof && f.name == 'main' {
|
||||
p.genln(p.print_prof_counters())
|
||||
@@ -420,12 +461,10 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
||||
if typ != 'void' && !p.returns && f.name != 'main' && f.name != 'WinMain' {
|
||||
p.error('$f.name must return "$typ"')
|
||||
}
|
||||
|
||||
if p.attr == 'live' && p.pref.is_so {
|
||||
//p.genln('// live_function body end')
|
||||
p.genln('pthread_mutex_unlock(&live_fn_mutex);')
|
||||
}
|
||||
|
||||
// {} closed correctly? scope_level should be 0
|
||||
if p.mod == 'main' {
|
||||
// println(p.cur_fn.scope_level)
|
||||
@@ -436,12 +475,16 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
||||
// Make sure all vars in this function are used (only in main for now)
|
||||
// if p.builtin_pkg || p.mod == 'os' ||p.mod=='http'{
|
||||
if p.mod != 'main' {
|
||||
p.genln('}')
|
||||
if !is_generic {
|
||||
p.genln('}')
|
||||
}
|
||||
return
|
||||
}
|
||||
p.check_unused_variables()
|
||||
p.cur_fn = EmptyFn
|
||||
p.genln('}')
|
||||
if !is_generic {
|
||||
p.genln('}')
|
||||
}
|
||||
}
|
||||
|
||||
fn (p mut Parser) check_unused_variables() {
|
||||
@@ -539,6 +582,9 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type
|
||||
}
|
||||
|
||||
fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type string) {
|
||||
if p.fileis('vtalk2.v') {
|
||||
//println('FN CALL k $f.name')
|
||||
}
|
||||
if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.pkg != p.mod {
|
||||
p.error('function `$f.name` is private')
|
||||
}
|
||||
@@ -550,7 +596,25 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
|
||||
p.error('use `malloc()` instead of `C.malloc()`')
|
||||
}
|
||||
}
|
||||
cgen_name := p.table.cgen_name(f)
|
||||
mut cgen_name := p.table.cgen_name(f)
|
||||
p.next()
|
||||
mut gen_type := ''
|
||||
if p.tok == .lt {
|
||||
p.check(.lt)
|
||||
gen_type = p.check_name()
|
||||
// `foo<Bar>()`
|
||||
// If we are in the first pass, we need to add `Bar` type to the generic function `foo`,
|
||||
// so that generic `foo`s body can be generated for each type in the second pass.
|
||||
if p.first_pass() {
|
||||
println('reging $gen_type in $f.name')
|
||||
p.table.register_generic_fn_type(f.name, gen_type)
|
||||
// Function bodies are skipped in the first passed, we only need to register the generic type here.
|
||||
return
|
||||
}
|
||||
cgen_name += '_' + gen_type
|
||||
|
||||
p.check(.gt)
|
||||
}
|
||||
// if p.pref.is_prof {
|
||||
// p.cur_fn.called_fns << cgen_name
|
||||
// }
|
||||
@@ -590,8 +654,8 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
|
||||
}
|
||||
p.cgen.set_placeholder(method_ph, '$cast $method_call')
|
||||
}
|
||||
p.next()
|
||||
p.fn_call_args(f)
|
||||
// foo<Bar>()
|
||||
p.fn_call_args(mut f)
|
||||
p.gen(')')
|
||||
p.calling_c = false
|
||||
// println('end of fn call typ=$f.typ')
|
||||
@@ -648,7 +712,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
||||
'\nreturn values instead: `foo(n mut int)` => `foo(n int) int`')
|
||||
}
|
||||
for name in names {
|
||||
if !p.first_run() && !p.table.known_type(typ) {
|
||||
if !p.first_pass() && !p.table.known_type(typ) {
|
||||
p.error('fn_args: unknown type $typ')
|
||||
}
|
||||
if is_mut {
|
||||
@@ -678,7 +742,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
||||
}
|
||||
|
||||
// foo *(1, 2, 3, mut bar)*
|
||||
fn (p mut Parser) fn_call_args(f *Fn) *Fn {
|
||||
fn (p mut Parser) fn_call_args(f mut Fn) *Fn {
|
||||
// p.gen('(')
|
||||
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
||||
// C func. # of args is not known
|
||||
@@ -762,10 +826,10 @@ fn (p mut Parser) fn_call_args(f *Fn) *Fn {
|
||||
p.error(error_msg)
|
||||
}
|
||||
p.cgen.resetln(p.cgen.cur_line.left(index))
|
||||
p.create_type_string(T, name)
|
||||
p.scanner.create_type_string(T, name)
|
||||
p.cgen.cur_line.replace(typ, '')
|
||||
p.next()
|
||||
return p.fn_call_args(f)
|
||||
return p.fn_call_args(mut f)
|
||||
}
|
||||
p.error(error_msg)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user