mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
generic functions
This commit is contained in:
parent
f63e24e51d
commit
8a31ee4b53
@ -62,22 +62,22 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
W = 50
|
width = 50
|
||||||
)
|
)
|
||||||
|
|
||||||
[live]
|
[live]
|
||||||
fn (game &Game) draw() {
|
fn (game &Game) draw() {
|
||||||
game.gg.draw_rect(game.x, game.y, W, W, gx.rgb(255, 0, 0))
|
game.gg.draw_rect(game.x, game.y, width, width, gx.rgb(255, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (game mut Game) run() {
|
fn (game mut Game) run() {
|
||||||
for {
|
for {
|
||||||
game.x += game.dx
|
game.x += game.dx
|
||||||
game.y += game.dy
|
game.y += game.dy
|
||||||
if game.y >= game.height - W || game.y <= 0 {
|
if game.y >= game.height - width || game.y <= 0 {
|
||||||
game.dy = - game.dy
|
game.dy = - game.dy
|
||||||
}
|
}
|
||||||
if game.x >= game.width - W || game.x <= 0 {
|
if game.x >= game.width - width || game.x <= 0 {
|
||||||
game.dx = - game.dx
|
game.dx = - game.dx
|
||||||
}
|
}
|
||||||
// Refresh
|
// Refresh
|
||||||
|
@ -41,17 +41,17 @@ fn (p mut Parser) comp_time() {
|
|||||||
stack++
|
stack++
|
||||||
} else if p.tok == .rcbr {
|
} else if p.tok == .rcbr {
|
||||||
stack--
|
stack--
|
||||||
}
|
}
|
||||||
if p.tok == .eof {
|
if p.tok == .eof {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if stack <= 0 && p.tok == .rcbr {
|
if stack <= 0 && p.tok == .rcbr {
|
||||||
//p.warn('exiting $stack')
|
//p.warn('exiting $stack')
|
||||||
p.next()
|
p.next()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p.statements_no_rcbr()
|
p.statements_no_rcbr()
|
||||||
}
|
}
|
||||||
@ -185,8 +185,9 @@ fn (p mut Parser) chash() {
|
|||||||
flag = flag.replace('@VROOT', p.vroot)
|
flag = flag.replace('@VROOT', p.vroot)
|
||||||
flag = flag.replace('@VMOD', v_modules_path)
|
flag = flag.replace('@VMOD', v_modules_path)
|
||||||
//p.log('adding flag "$flag"')
|
//p.log('adding flag "$flag"')
|
||||||
_ = p.table.parse_cflag(flag, p.mod) or {
|
_p := p.table.parse_cflag(flag, p.mod) or {
|
||||||
p.error_with_token_index(err, p.cur_tok_index()-1)
|
p.error_with_token_index(err, p.cur_tok_index()-1)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -219,16 +220,16 @@ fn (p mut Parser) chash() {
|
|||||||
$if js {
|
$if js {
|
||||||
for p.tok != .eof {
|
for p.tok != .eof {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
} $else {
|
} $else {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$if !js {
|
$if !js {
|
||||||
if !p.can_chash {
|
if !p.can_chash {
|
||||||
println('hash="$hash"')
|
println('hash="$hash"')
|
||||||
println(hash.starts_with('include'))
|
if hash.starts_with('include') { println("include") } else {}
|
||||||
p.error('bad token `#` (embedding C code is no longer supported)')
|
p.error('bad token `#` (embedding C code is no longer supported)')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,7 +308,7 @@ fn (p mut Parser) gen_struct_str(typ Type) {
|
|||||||
is_public: true
|
is_public: true
|
||||||
receiver_typ: typ.name
|
receiver_typ: typ.name
|
||||||
})
|
})
|
||||||
|
|
||||||
mut sb := strings.new_builder(typ.fields.len * 20)
|
mut sb := strings.new_builder(typ.fields.len * 20)
|
||||||
sb.writeln('fn (a $typ.name) str() string {\nreturn')
|
sb.writeln('fn (a $typ.name) str() string {\nreturn')
|
||||||
sb.writeln("'{")
|
sb.writeln("'{")
|
||||||
|
@ -32,10 +32,33 @@ mut:
|
|||||||
returns_error bool
|
returns_error bool
|
||||||
is_decl bool // type myfn fn(int, int)
|
is_decl bool // type myfn fn(int, int)
|
||||||
defer_text []string
|
defer_text []string
|
||||||
//gen_types []string
|
is_generic bool
|
||||||
|
type_pars []string
|
||||||
|
type_inst []TypeInst
|
||||||
|
dispatch_of TypeInst // current type inst of this generic instance
|
||||||
|
body_idx int // idx of the first body statement
|
||||||
fn_name_token_idx int // used by error reporting
|
fn_name_token_idx int // used by error reporting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TypeInst {
|
||||||
|
mut:
|
||||||
|
// an instantiation of generic params (e.g. ["int","int","double"])
|
||||||
|
inst map[string]string
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a []TypeInst) str() string {
|
||||||
|
mut r := []string
|
||||||
|
for t in a {
|
||||||
|
mut s := ' | '
|
||||||
|
for k in t.inst.keys() {
|
||||||
|
s += k+' -> '+ t.inst[k] +' | '
|
||||||
|
}
|
||||||
|
r << s
|
||||||
|
}
|
||||||
|
return r.str()
|
||||||
|
}
|
||||||
|
|
||||||
fn (p &Parser) find_var(name string) ?Var {
|
fn (p &Parser) find_var(name string) ?Var {
|
||||||
for i in 0 .. p.var_idx {
|
for i in 0 .. p.var_idx {
|
||||||
if p.local_vars[i].name == name {
|
if p.local_vars[i].name == name {
|
||||||
@ -71,21 +94,21 @@ fn (p mut Parser) open_scope() {
|
|||||||
fn (p mut Parser) mark_var_used(v Var) {
|
fn (p mut Parser) mark_var_used(v Var) {
|
||||||
if v.idx == -1 || v.idx >= p.local_vars.len {
|
if v.idx == -1 || v.idx >= p.local_vars.len {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.local_vars[v.idx].is_used = true
|
p.local_vars[v.idx].is_used = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) mark_var_returned(v Var) {
|
fn (p mut Parser) mark_var_returned(v Var) {
|
||||||
if v.idx == -1 || v.idx >= p.local_vars.len {
|
if v.idx == -1 || v.idx >= p.local_vars.len {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.local_vars[v.idx].is_returned = true
|
p.local_vars[v.idx].is_returned = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) mark_var_changed(v Var) {
|
fn (p mut Parser) mark_var_changed(v Var) {
|
||||||
if v.idx == -1 || v.idx >= p.local_vars.len {
|
if v.idx == -1 || v.idx >= p.local_vars.len {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.local_vars[v.idx].is_changed = true
|
p.local_vars[v.idx].is_changed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,15 +118,22 @@ fn (p mut Parser) mark_arg_moved(v Var) {
|
|||||||
//println('setting f $p.cur_fn.name arg $arg.name to is_mut')
|
//println('setting f $p.cur_fn.name arg $arg.name to is_mut')
|
||||||
p.cur_fn.args[i].is_moved = true
|
p.cur_fn.args[i].is_moved = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.table.fns[p.cur_fn.name] = p.cur_fn
|
p.table.fns[p.cur_fn.name] = p.cur_fn
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) known_var(name string) bool {
|
fn (p mut Parser) known_var(name string) bool {
|
||||||
_ = p.find_var(name) or {
|
_ = p.find_var(name) or {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) known_var_check_new_var(name string) bool {
|
||||||
|
_ = p.find_var_check_new_var(name) or {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +168,7 @@ fn (p mut Parser) clear_vars() {
|
|||||||
fn (p mut Parser) fn_decl() {
|
fn (p mut Parser) fn_decl() {
|
||||||
p.clear_vars() // clear local vars every time a new fn is started
|
p.clear_vars() // clear local vars every time a new fn is started
|
||||||
p.fgen('fn ')
|
p.fgen('fn ')
|
||||||
|
|
||||||
//defer { p.fgenln('\n') }
|
//defer { p.fgenln('\n') }
|
||||||
// If we are in the first pass, create a new function.
|
// If we are in the first pass, create a new function.
|
||||||
// In the second pass fetch the one we created.
|
// In the second pass fetch the one we created.
|
||||||
@ -149,12 +179,12 @@ fn (p mut Parser) fn_decl() {
|
|||||||
is_public: p.tok == .key_pub
|
is_public: p.tok == .key_pub
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
mut f := Fn{
|
mut f := Fn{
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
is_public: p.tok == .key_pub
|
is_public: p.tok == .key_pub
|
||||||
}
|
}
|
||||||
is_live := p.attr == 'live' && !p.pref.is_so && p.pref.is_live
|
is_live := p.attr == 'live' && !p.pref.is_so && p.pref.is_live
|
||||||
if p.attr == 'live' && p.first_pass() && !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')
|
println('INFO: run `v -live program.v` if you want to use [live] functions')
|
||||||
@ -243,7 +273,7 @@ fn (p mut Parser) fn_decl() {
|
|||||||
}
|
}
|
||||||
if f.name[0] == `_` {
|
if f.name[0] == `_` {
|
||||||
p.error('function names cannot start with `_`, use snake_case instead')
|
p.error('function names cannot start with `_`, use snake_case instead')
|
||||||
}
|
}
|
||||||
if f.name.contains('__') {
|
if f.name.contains('__') {
|
||||||
p.error('function names cannot contain double underscores, use single underscores instead')
|
p.error('function names cannot contain double underscores, use single underscores instead')
|
||||||
}
|
}
|
||||||
@ -272,21 +302,23 @@ fn (p mut Parser) fn_decl() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generic?
|
// Generic?
|
||||||
mut is_generic := false
|
|
||||||
if p.tok == .lt {
|
if p.tok == .lt {
|
||||||
is_generic = true
|
f.is_generic = true
|
||||||
p.next()
|
p.next()
|
||||||
gen_type := p.check_name()
|
for {
|
||||||
if gen_type != 'T' {
|
type_par := p.check_name()
|
||||||
p.error('only `T` is allowed as a generic type for now')
|
if type_par.len > 1 || !(type_par in reserved_type_param_names) {
|
||||||
|
p.error('type parameters must be single-character, upper-case letters of the following set: $reserved_type_param_names')
|
||||||
|
}
|
||||||
|
if type_par in f.type_pars {
|
||||||
|
p.error('redeclaration of type parameter `$type_par`')
|
||||||
|
}
|
||||||
|
f.type_pars << type_par
|
||||||
|
if p.tok == .gt { break }
|
||||||
|
p.check(.comma)
|
||||||
}
|
}
|
||||||
|
p.set_current_fn(f)
|
||||||
p.check(.gt)
|
p.check(.gt)
|
||||||
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 (...)
|
// Args (...)
|
||||||
p.fn_args(mut f)
|
p.fn_args(mut f)
|
||||||
@ -326,13 +358,7 @@ fn (p mut Parser) fn_decl() {
|
|||||||
p.error_with_token_index('fn main must have no arguments and no return values', f.fn_name_token_idx)
|
p.error_with_token_index('fn main must have no arguments and no return values', f.fn_name_token_idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dll_export_linkage := if p.pref.ccompiler == 'msvc' && p.attr == 'live' && p.pref.is_so {
|
dll_export_linkage := p.get_linkage_prefix()
|
||||||
'__declspec(dllexport) '
|
|
||||||
} else if p.attr == 'inline' {
|
|
||||||
'static inline '
|
|
||||||
} else {
|
|
||||||
''
|
|
||||||
}
|
|
||||||
if !p.is_vweb {
|
if !p.is_vweb {
|
||||||
p.set_current_fn( f )
|
p.set_current_fn( f )
|
||||||
}
|
}
|
||||||
@ -345,25 +371,18 @@ fn (p mut Parser) fn_decl() {
|
|||||||
if p.pref.obfuscate {
|
if p.pref.obfuscate {
|
||||||
p.genln('; // $f.name')
|
p.genln('; // $f.name')
|
||||||
}
|
}
|
||||||
// Generate this function's body for all generic types
|
// Generic functions are inserted as needed from the call site
|
||||||
if is_generic {
|
if f.is_generic {
|
||||||
gen_types := p.table.fn_gen_types(f.name)
|
if p.first_pass() {
|
||||||
// Remember current scanner position, go back here for each type
|
f.body_idx = p.cur_tok_index()+1
|
||||||
// TODO remove this once tokens are cached in `new_parser()`
|
p.table.register_fn(f)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
p.check_unused_variables()
|
||||||
else {
|
p.set_current_fn( EmptyFn )
|
||||||
|
p.returns = false
|
||||||
|
p.skip_fn_body()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
p.gen_fn_decl(f, typ, str_args)
|
p.gen_fn_decl(f, typ, str_args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -441,10 +460,6 @@ fn (p mut Parser) fn_decl() {
|
|||||||
f.defer_text[f.scope_level] = ' ${cgen_name}_time += time__ticks() - _PROF_START;'
|
f.defer_text[f.scope_level] = ' ${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.statements_no_rcbr()
|
||||||
//p.cgen.nogen = false
|
//p.cgen.nogen = false
|
||||||
// Print counting result after all statements in main
|
// Print counting result after all statements in main
|
||||||
@ -473,17 +488,13 @@ fn (p mut Parser) fn_decl() {
|
|||||||
}
|
}
|
||||||
// Make sure all vars in this function are used (only in main for now)
|
// Make sure all vars in this function are used (only in main for now)
|
||||||
if p.mod != 'main' {
|
if p.mod != 'main' {
|
||||||
if !is_generic {
|
p.genln('}')
|
||||||
p.genln('}')
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
p.genln('}')
|
||||||
p.check_unused_variables()
|
p.check_unused_variables()
|
||||||
p.set_current_fn( EmptyFn )
|
p.set_current_fn( EmptyFn )
|
||||||
p.returns = false
|
p.returns = false
|
||||||
if !is_generic {
|
|
||||||
p.genln('}')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
@ -515,6 +526,16 @@ fn (p mut Parser) skip_fn_body() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (p Parser) get_linkage_prefix() string {
|
||||||
|
return if p.pref.ccompiler == 'msvc' && p.attr == 'live' && p.pref.is_so {
|
||||||
|
'__declspec(dllexport) '
|
||||||
|
} else if p.attr == 'inline' {
|
||||||
|
'static inline '
|
||||||
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (p mut Parser) check_unused_variables() {
|
fn (p mut Parser) check_unused_variables() {
|
||||||
for var in p.local_vars {
|
for var in p.local_vars {
|
||||||
if var.name == '' {
|
if var.name == '' {
|
||||||
@ -609,7 +630,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type
|
|||||||
}
|
}
|
||||||
|
|
||||||
// p.tok == fn_name
|
// p.tok == fn_name
|
||||||
fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type string) {
|
fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type string) {
|
||||||
if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.mod != p.mod {
|
if !f.is_public && !f.is_c && !p.pref.is_test && !f.is_interface && f.mod != p.mod {
|
||||||
if f.name == 'contains' {
|
if f.name == 'contains' {
|
||||||
println('use `value in numbers` instead of `numbers.contains(value)`')
|
println('use `value in numbers` instead of `numbers.contains(value)`')
|
||||||
@ -624,40 +645,48 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
|
|||||||
p.error('use `malloc()` instead of `C.malloc()`')
|
p.error('use `malloc()` instead of `C.malloc()`')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mut cgen_name := p.table.fn_gen_name(f)
|
cgen_name := p.table.fn_gen_name(f)
|
||||||
p.next()
|
p.next() // fn name
|
||||||
mut gen_type := ''
|
|
||||||
if p.tok == .lt {
|
if p.tok == .lt {
|
||||||
p.check(.lt)
|
mut i := p.token_idx
|
||||||
gen_type = p.check_name()
|
for {
|
||||||
// run<T> => run_App
|
if p.tokens[i].tok == .gt {
|
||||||
if gen_type == 'T' && p.cur_gen_type != '' {
|
p.error('explicit type arguments are not allowed; remove `<...>`')
|
||||||
gen_type = p.cur_gen_type
|
} else if p.tokens[i].tok == .lpar {
|
||||||
|
// probably a typo, do not concern the user with the above error message
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
}
|
}
|
||||||
// `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('registering $gen_type in $f.name fname=$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 {
|
// if p.pref.is_prof {
|
||||||
// p.cur_fn.called_fns << cgen_name
|
// p.cur_fn.called_fns << cgen_name
|
||||||
// }
|
// }
|
||||||
// Normal function call
|
|
||||||
if !f.is_method {
|
$if windows { // TODO fix segfault caused by `dispatch_generic_fn_instance` on Windows
|
||||||
p.gen(cgen_name)
|
if f.is_generic {
|
||||||
p.gen('(')
|
p.check(.lpar)
|
||||||
// p.fgen(f.name)
|
mut b := 1
|
||||||
|
for b > 0 {
|
||||||
|
if p.tok == .rpar {
|
||||||
|
b -= 1
|
||||||
|
} else if p.tok == .lpar {
|
||||||
|
b += 1
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
p.gen('/* SKIPPED */')
|
||||||
|
p.warn('skipped call to generic function `$f.name`\n\tReason: generic functions are currently broken on Windows 10\n')
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a method placeholder,
|
// If we have a method placeholder,
|
||||||
// we need to preappend "method(receiver, ...)"
|
// we need to preappend "method(receiver, ...)"
|
||||||
else {
|
if f.is_method {
|
||||||
|
if f.is_generic {
|
||||||
|
p.error('generic methods are not yet implemented')
|
||||||
|
}
|
||||||
receiver := f.args.first()
|
receiver := f.args.first()
|
||||||
//println('r=$receiver.typ RT=$receiver_type')
|
//println('r=$receiver.typ RT=$receiver_type')
|
||||||
if receiver.is_mut && !p.expr_var.is_mut {
|
if receiver.is_mut && !p.expr_var.is_mut {
|
||||||
@ -672,10 +701,23 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
|
|||||||
if !p.expr_var.is_changed {
|
if !p.expr_var.is_changed {
|
||||||
p.mark_var_changed(p.expr_var)
|
p.mark_var_changed(p.expr_var)
|
||||||
}
|
}
|
||||||
p.gen_method_call(receiver_type, f.typ, cgen_name, receiver, method_ph)
|
met_name := if f.is_generic { f.name } else { cgen_name }
|
||||||
|
p.gen_method_call(receiver_type, f.typ, met_name, receiver, method_ph)
|
||||||
|
} else {
|
||||||
|
// Normal function call
|
||||||
|
p.gen('$cgen_name (')
|
||||||
}
|
}
|
||||||
|
|
||||||
// foo<Bar>()
|
// foo<Bar>()
|
||||||
|
// if f is generic, the name is changed to a suitable instance in dispatch_generic_fn_instance()
|
||||||
|
// we then replace `cgen_name` with the instance's name
|
||||||
|
generic := f.is_generic
|
||||||
p.fn_call_args(mut f)
|
p.fn_call_args(mut f)
|
||||||
|
if generic {
|
||||||
|
p.cgen.resetln(p.cgen.cur_line.replace('$cgen_name (', '$f.name ('))
|
||||||
|
// println('calling inst $f.name: $p.cgen.cur_line')
|
||||||
|
}
|
||||||
|
|
||||||
p.gen(')')
|
p.gen(')')
|
||||||
p.calling_c = false
|
p.calling_c = false
|
||||||
// println('end of fn call typ=$f.typ')
|
// println('end of fn call typ=$f.typ')
|
||||||
@ -726,7 +768,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|||||||
p.fspace()
|
p.fspace()
|
||||||
is_mut := p.tok == .key_mut
|
is_mut := p.tok == .key_mut
|
||||||
if is_mut {
|
if is_mut {
|
||||||
p.next()
|
p.check(.key_mut)
|
||||||
}
|
}
|
||||||
mut typ := ''
|
mut typ := ''
|
||||||
// variadic arg
|
// variadic arg
|
||||||
@ -745,7 +787,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|||||||
} else {
|
} else {
|
||||||
typ = p.get_type()
|
typ = p.get_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
p.check_and_register_used_imported_type(typ)
|
p.check_and_register_used_imported_type(typ)
|
||||||
if is_mut && is_primitive_type(typ) {
|
if is_mut && is_primitive_type(typ) {
|
||||||
p.error('mutable arguments are only allowed for arrays, maps, and structs.' +
|
p.error('mutable arguments are only allowed for arrays, maps, and structs.' +
|
||||||
@ -771,7 +813,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|||||||
f.args << v
|
f.args << v
|
||||||
}
|
}
|
||||||
if p.tok == .comma {
|
if p.tok == .comma {
|
||||||
p.next()
|
p.check(.comma)
|
||||||
}
|
}
|
||||||
// unnamed (C definition)
|
// unnamed (C definition)
|
||||||
if p.tok == .ellipsis {
|
if p.tok == .ellipsis {
|
||||||
@ -788,7 +830,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// foo *(1, 2, 3, mut bar)*
|
// foo *(1, 2, 3, mut bar)*
|
||||||
fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
fn (p mut Parser) fn_call_args(f mut Fn) {
|
||||||
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
||||||
// C func. # of args is not known
|
// C func. # of args is not known
|
||||||
p.check(.lpar)
|
p.check(.lpar)
|
||||||
@ -808,14 +850,14 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
|||||||
// Cast V byteptr to C char* (byte is unsigned in V, that led to C warnings)
|
// Cast V byteptr to C char* (byte is unsigned in V, that led to C warnings)
|
||||||
if typ == 'byte*' {
|
if typ == 'byte*' {
|
||||||
p.cgen.set_placeholder(ph, '(char*)')
|
p.cgen.set_placeholder(ph, '(char*)')
|
||||||
}
|
}
|
||||||
if p.tok == .comma {
|
if p.tok == .comma {
|
||||||
p.gen(', ')
|
p.gen(', ')
|
||||||
p.check(.comma)
|
p.check(.comma)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.check(.rpar)
|
p.check(.rpar)
|
||||||
return f
|
return
|
||||||
}
|
}
|
||||||
// add debug information to panic when -g arg is passed
|
// add debug information to panic when -g arg is passed
|
||||||
if p.v.pref.is_debug && f.name == 'panic' && !p.is_js {
|
if p.v.pref.is_debug && f.name == 'panic' && !p.is_js {
|
||||||
@ -827,6 +869,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
|||||||
'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '
|
'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
mut saved_args := []string
|
||||||
for i, arg in f.args {
|
for i, arg in f.args {
|
||||||
// Receiver is the first arg
|
// Receiver is the first arg
|
||||||
// Skip the receiver, because it was already generated in the expression
|
// Skip the receiver, because it was already generated in the expression
|
||||||
@ -857,7 +900,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
|||||||
if i < f.args.len - 1 {
|
if i < f.args.len - 1 {
|
||||||
dots_example = dots_example + ',..'
|
dots_example = dots_example + ',..'
|
||||||
}
|
}
|
||||||
p.error('`$arg.name` is a mutable argument, you need to provide `mut`: `$f.name($dots_example)`')
|
p.error('`$arg.name` is a mutable argument, you need to provide `mut`: `$f.name($dots_example)`')
|
||||||
}
|
}
|
||||||
if p.peek() != .name {
|
if p.peek() != .name {
|
||||||
p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`')
|
p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`')
|
||||||
@ -876,7 +919,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
|||||||
clone := p.pref.autofree && arg.typ == 'string' && arg.is_moved && p.mod != 'builtin'
|
clone := p.pref.autofree && arg.typ == 'string' && arg.is_moved && p.mod != 'builtin'
|
||||||
if clone {
|
if clone {
|
||||||
p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(')
|
p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(')
|
||||||
}
|
}
|
||||||
mut typ := p.bool_expression()
|
mut typ := p.bool_expression()
|
||||||
if typ.starts_with('...') { typ = typ.right(3) }
|
if typ.starts_with('...') { typ = typ.right(3) }
|
||||||
if clone {
|
if clone {
|
||||||
@ -923,7 +966,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
|||||||
p.cgen.set_placeholder(ph, '${typ}_str(')
|
p.cgen.set_placeholder(ph, '${typ}_str(')
|
||||||
p.gen(')')
|
p.gen(')')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
error_msg := ('`$typ` needs to have method `str() string` to be printable')
|
error_msg := ('`$typ` needs to have method `str() string` to be printable')
|
||||||
p.error(error_msg)
|
p.error(error_msg)
|
||||||
}
|
}
|
||||||
@ -937,11 +980,15 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
|||||||
got_ptr := got.ends_with('*')
|
got_ptr := got.ends_with('*')
|
||||||
exp_ptr := expected.ends_with('*')
|
exp_ptr := expected.ends_with('*')
|
||||||
// println('fn arg got="$got" exp="$expected"')
|
// println('fn arg got="$got" exp="$expected"')
|
||||||
if !p.check_types_no_throw(got, expected) {
|
type_mismatch := !p.check_types_no_throw(got, expected)
|
||||||
|
if type_mismatch && f.is_generic {
|
||||||
|
// println("argument `$arg.name` is generic")
|
||||||
|
saved_args << got
|
||||||
|
} else if type_mismatch {
|
||||||
mut j := i
|
mut j := i
|
||||||
if f.is_method {
|
if f.is_method {
|
||||||
j--
|
j--
|
||||||
}
|
}
|
||||||
mut nr := '${i+1}th'
|
mut nr := '${i+1}th'
|
||||||
if j == 0 {
|
if j == 0 {
|
||||||
nr = 'first'
|
nr = 'first'
|
||||||
@ -949,9 +996,11 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
|||||||
nr = 'second'
|
nr = 'second'
|
||||||
} else if j == 2 {
|
} else if j == 2 {
|
||||||
nr = 'third'
|
nr = 'third'
|
||||||
}
|
}
|
||||||
p.error('cannot use type `$typ` as type `$arg.typ` in $nr ' +
|
p.error('cannot use type `$typ` as type `$arg.typ` in $nr ' +
|
||||||
'argument to `$f.name()`')
|
'argument to `$f.name()`')
|
||||||
|
} else {
|
||||||
|
saved_args << ''
|
||||||
}
|
}
|
||||||
is_interface := p.table.is_interface(arg.typ)
|
is_interface := p.table.is_interface(arg.typ)
|
||||||
// Automatically add `&` or `*` before an argument.
|
// Automatically add `&` or `*` before an argument.
|
||||||
@ -1019,7 +1068,105 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn {
|
|||||||
p.error('wrong number of arguments for fn `$f.name`: expected $f.args.len, but got more')
|
p.error('wrong number of arguments for fn `$f.name`: expected $f.args.len, but got more')
|
||||||
}
|
}
|
||||||
p.check(.rpar)
|
p.check(.rpar)
|
||||||
return f // TODO is return f right?
|
if f.is_generic {
|
||||||
|
type_map := p.extract_type_inst(f, saved_args)
|
||||||
|
p.dispatch_generic_fn_instance(mut f, type_map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From a given generic function and an argument list matching its signature,
|
||||||
|
// create a type instantiation
|
||||||
|
fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst {
|
||||||
|
mut r := TypeInst{}
|
||||||
|
mut i := 0
|
||||||
|
mut args := args_
|
||||||
|
args << f.typ
|
||||||
|
for ai, e in args {
|
||||||
|
if e == '' { continue }
|
||||||
|
tp := f.type_pars[i]
|
||||||
|
mut ti := e
|
||||||
|
if ti.starts_with('fn (') {
|
||||||
|
fn_args := ti.right(4).all_before(') ').split(',')
|
||||||
|
mut found := false
|
||||||
|
for fa_ in fn_args {
|
||||||
|
mut fa := fa_
|
||||||
|
for fa.starts_with('array_') { fa = fa.right(6) }
|
||||||
|
if fa == tp {
|
||||||
|
r.inst[tp] = fa
|
||||||
|
found = true
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found { continue }
|
||||||
|
ti = ti.all_after(') ')
|
||||||
|
}
|
||||||
|
for ti.starts_with('array_') { ti = ti.right(6) }
|
||||||
|
if r.inst[tp] != '' {
|
||||||
|
if r.inst[tp] != ti {
|
||||||
|
p.error('type parameter `$tp` has type ${r.inst[tp]}, not `$ti`')
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// println("extracted $tp => $ti")
|
||||||
|
r.inst[tp] = ti
|
||||||
|
i += 1
|
||||||
|
if i >= f.type_pars.len { break }
|
||||||
|
}
|
||||||
|
if r.inst[f.typ] == '' && f.typ in f.type_pars {
|
||||||
|
r.inst[f.typ] = '_ANYTYPE_'
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace type params of a given generic function using a TypeInst
|
||||||
|
fn (p mut Parser) replace_type_params(f &Fn, ti TypeInst) []string {
|
||||||
|
mut sig := []string
|
||||||
|
for a in f.args {
|
||||||
|
sig << a.typ
|
||||||
|
}
|
||||||
|
sig << f.typ
|
||||||
|
mut r := []string
|
||||||
|
for _, a in sig {
|
||||||
|
mut fi := a
|
||||||
|
mut fr := ''
|
||||||
|
if fi.starts_with('fn (') {
|
||||||
|
fr += 'fn ('
|
||||||
|
mut fn_args := fi.right(4).all_before(') ').split(',')
|
||||||
|
fn_args << fi.all_after(') ')
|
||||||
|
for i, fa_ in fn_args {
|
||||||
|
mut fna := fa_.trim_space()
|
||||||
|
for fna.starts_with('array_') {
|
||||||
|
fna = fna.right(6)
|
||||||
|
fr += 'array_'
|
||||||
|
}
|
||||||
|
if fna in ti.inst.keys() {
|
||||||
|
fr += ti.inst[fna]
|
||||||
|
} else {
|
||||||
|
fr += fna
|
||||||
|
}
|
||||||
|
if i <= fn_args.len-3 {
|
||||||
|
fr += ','
|
||||||
|
} else if i == fn_args.len-2 {
|
||||||
|
fr += ') '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r << fr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for fi.starts_with('array_') {
|
||||||
|
fi = fi.right(6)
|
||||||
|
fr += 'array_'
|
||||||
|
}
|
||||||
|
if fi in ti.inst.keys() {
|
||||||
|
fr += ti.inst[fi]
|
||||||
|
// println("replaced $a => $fr")
|
||||||
|
} else {
|
||||||
|
fr += fi
|
||||||
|
}
|
||||||
|
r << fr
|
||||||
|
}
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) fn_register_vargs_stuct(f &Fn, typ string, values []string) string {
|
fn (p mut Parser) fn_register_vargs_stuct(f &Fn, typ string, values []string) string {
|
||||||
@ -1067,7 +1214,7 @@ fn (p mut Parser) fn_gen_caller_vargs(f mut Fn) {
|
|||||||
}
|
}
|
||||||
vargs_struct := p.fn_register_vargs_stuct(f, varg_def_type, values)
|
vargs_struct := p.fn_register_vargs_stuct(f, varg_def_type, values)
|
||||||
p.cgen.gen('&($vargs_struct){.len=$values.len,.args={'+values.join(',')+'}}')
|
p.cgen.gen('&($vargs_struct){.len=$values.len,.args={'+values.join(',')+'}}')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) register_multi_return_stuct(types []string) string {
|
fn (p mut Parser) register_multi_return_stuct(types []string) string {
|
||||||
@ -1085,6 +1232,118 @@ fn (p mut Parser) register_multi_return_stuct(types []string) string {
|
|||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti TypeInst) {
|
||||||
|
$if windows {
|
||||||
|
p.error('feature disabled on Windows')
|
||||||
|
}
|
||||||
|
mut new_inst := true
|
||||||
|
for e in f.type_inst {
|
||||||
|
if e.inst.str() == ti.inst.str() {
|
||||||
|
new_inst = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !new_inst {
|
||||||
|
f.name = f.name + '_T'
|
||||||
|
for k in ti.inst.keys() {
|
||||||
|
f.name = f.name + '_' + type_to_safe_str(ti.inst[k])
|
||||||
|
}
|
||||||
|
_f := p.table.find_fn(f.name) or {
|
||||||
|
p.error('function instance `$f.name` not found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.args = _f.args
|
||||||
|
f.typ = _f.typ
|
||||||
|
f.is_generic = false
|
||||||
|
f.type_inst = []TypeInst
|
||||||
|
f.dispatch_of = ti
|
||||||
|
// println('using existing inst $f.name(${f.str_args(p.table)}) $f.typ')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.type_inst << ti
|
||||||
|
p.table.register_fn(f)
|
||||||
|
// Remember current scanner position, go back here for each type instance
|
||||||
|
// TODO remove this once tokens are cached in `new_parser()`
|
||||||
|
saved_tok_idx := p.cur_tok_index()
|
||||||
|
saved_fn := p.cur_fn
|
||||||
|
saved_var_idx := p.var_idx
|
||||||
|
saved_local_vars := p.local_vars
|
||||||
|
p.clear_vars()
|
||||||
|
saved_line := p.cgen.cur_line
|
||||||
|
saved_lines := p.cgen.lines
|
||||||
|
saved_is_tmp := p.cgen.is_tmp
|
||||||
|
saved_tmp_line := p.cgen.tmp_line
|
||||||
|
returns := p.returns // should be always false
|
||||||
|
|
||||||
|
f.name = f.name + '_T'
|
||||||
|
for k in ti.inst.keys() {
|
||||||
|
f.name = f.name + '_' + type_to_safe_str(ti.inst[k])
|
||||||
|
}
|
||||||
|
f.is_generic = false // the instance is a normal function
|
||||||
|
f.type_inst = []TypeInst
|
||||||
|
f.scope_level = 0
|
||||||
|
f.dispatch_of = ti
|
||||||
|
old_args := f.args
|
||||||
|
new_types := p.replace_type_params(f, ti)
|
||||||
|
f.args = []Var
|
||||||
|
for i in 0..new_types.len-1 {
|
||||||
|
mut v := old_args[i]
|
||||||
|
v.typ = new_types[i]
|
||||||
|
f.args << v
|
||||||
|
}
|
||||||
|
f.typ = new_types.last()
|
||||||
|
if f.typ in f.type_pars { f.typ = '_ANYTYPE_' }
|
||||||
|
|
||||||
|
if f.typ in ti.inst {
|
||||||
|
f.typ = ti.inst[f.typ]
|
||||||
|
}
|
||||||
|
p.table.register_fn(f)
|
||||||
|
// println("generating gen inst $f.name(${f.str_args(p.table)}) $f.typ : $ti.inst")
|
||||||
|
|
||||||
|
p.cgen.is_tmp = false
|
||||||
|
p.returns = false
|
||||||
|
p.cgen.tmp_line = ''
|
||||||
|
p.cgen.cur_line = ''
|
||||||
|
p.cgen.lines = []string
|
||||||
|
p.cur_fn = *f
|
||||||
|
for arg in f.args {
|
||||||
|
p.register_var(arg)
|
||||||
|
}
|
||||||
|
p.token_idx = f.body_idx-1
|
||||||
|
p.next() // re-initializes the parser properly
|
||||||
|
str_args := f.str_args(p.table)
|
||||||
|
|
||||||
|
p.in_dispatch = true
|
||||||
|
p.genln('${p.get_linkage_prefix()}$f.typ $f.name($str_args) {')
|
||||||
|
// p.genln('/* generic fn instance $f.name : $ti.inst */')
|
||||||
|
p.statements()
|
||||||
|
p.in_dispatch = false
|
||||||
|
|
||||||
|
if f.typ == '_ANYTYPE_' {
|
||||||
|
f.typ = p.cur_fn.typ
|
||||||
|
f.name = f.name.replace('_ANYTYPE_', type_to_safe_str(f.typ))
|
||||||
|
p.cgen.lines[0] = p.cgen.lines[0].replace('_ANYTYPE_', f.typ)
|
||||||
|
p.table.register_fn(f)
|
||||||
|
}
|
||||||
|
for l in p.cgen.lines {
|
||||||
|
p.cgen.fns << l
|
||||||
|
}
|
||||||
|
|
||||||
|
p.token_idx = saved_tok_idx-1
|
||||||
|
p.next()
|
||||||
|
p.check(.rpar) // end of the arg list which caused this dispatch
|
||||||
|
p.cur_fn = saved_fn
|
||||||
|
p.var_idx = saved_var_idx
|
||||||
|
p.local_vars = saved_local_vars
|
||||||
|
p.cgen.lines = saved_lines
|
||||||
|
p.cgen.cur_line = saved_line
|
||||||
|
p.cgen.is_tmp = saved_is_tmp
|
||||||
|
p.cgen.tmp_line = saved_tmp_line
|
||||||
|
p.returns = false
|
||||||
|
}
|
||||||
|
|
||||||
// "fn (int, string) int"
|
// "fn (int, string) int"
|
||||||
fn (f &Fn) typ_str() string {
|
fn (f &Fn) typ_str() string {
|
||||||
mut sb := strings.new_builder(50)
|
mut sb := strings.new_builder(50)
|
||||||
@ -1162,7 +1421,7 @@ fn (fns []Fn) contains(f Fn) bool {
|
|||||||
for ff in fns {
|
for ff in fns {
|
||||||
if ff.name == f.name {
|
if ff.name == f.name {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -89,14 +89,14 @@ pub mut:
|
|||||||
is_run bool
|
is_run bool
|
||||||
show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c
|
show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c
|
||||||
sanitize bool // use Clang's new "-fsanitize" option
|
sanitize bool // use Clang's new "-fsanitize" option
|
||||||
|
|
||||||
is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
|
is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
|
||||||
is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly).
|
is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly).
|
||||||
is_keep_c bool // -keep_c , tell v to leave the generated .tmp.c alone (since by default v will delete them after c backend finishes)
|
is_keep_c bool // -keep_c , tell v to leave the generated .tmp.c alone (since by default v will delete them after c backend finishes)
|
||||||
// NB: passing -cg instead of -g will set is_vlines to false and is_g to true, thus making v generate cleaner C files,
|
// NB: passing -cg instead of -g will set is_vlines to false and is_g to true, thus making v generate cleaner C files,
|
||||||
// which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks).
|
// which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks).
|
||||||
is_cache bool // turns on v usage of the module cache to speed up compilation.
|
is_cache bool // turns on v usage of the module cache to speed up compilation.
|
||||||
|
|
||||||
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
|
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
|
||||||
no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers)
|
no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers)
|
||||||
cflags string // Additional options which will be passed to the C compiler.
|
cflags string // Additional options which will be passed to the C compiler.
|
||||||
@ -114,7 +114,7 @@ pub mut:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Should be called by main at the end of the compilation process, to cleanup
|
// Should be called by main at the end of the compilation process, to cleanup
|
||||||
pub fn (v mut V) finalize_compilation(){
|
pub fn (v mut V) finalize_compilation(){
|
||||||
// TODO remove
|
// TODO remove
|
||||||
if v.pref.autofree {
|
if v.pref.autofree {
|
||||||
println('started freeing v struct')
|
println('started freeing v struct')
|
||||||
@ -126,12 +126,12 @@ pub fn (v mut V) finalize_compilation(){
|
|||||||
//f.local_vars.free()
|
//f.local_vars.free()
|
||||||
f.args.free()
|
f.args.free()
|
||||||
//f.defer_text.free()
|
//f.defer_text.free()
|
||||||
}
|
}
|
||||||
v.table.fns.free()
|
v.table.fns.free()
|
||||||
free(v.table)
|
free(v.table)
|
||||||
//for p in parsers {}
|
//for p in parsers {}
|
||||||
println('done!')
|
println('done!')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (v mut V) add_parser(parser Parser) {
|
pub fn (v mut V) add_parser(parser Parser) {
|
||||||
@ -142,7 +142,7 @@ pub fn (v &V) get_file_parser_index(file string) ?int {
|
|||||||
for i, p in v.parsers {
|
for i, p in v.parsers {
|
||||||
if os.realpath(p.file_path_id) == os.realpath(file) {
|
if os.realpath(p.file_path_id) == os.realpath(file) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return error('parser for "$file" not found')
|
return error('parser for "$file" not found')
|
||||||
}
|
}
|
||||||
@ -184,11 +184,11 @@ pub fn (v mut V) compile() {
|
|||||||
println('\nparsers:')
|
println('\nparsers:')
|
||||||
for q in v.parsers {
|
for q in v.parsers {
|
||||||
println(q.file_name)
|
println(q.file_name)
|
||||||
}
|
}
|
||||||
println('\nfiles:')
|
println('\nfiles:')
|
||||||
for q in v.files {
|
for q in v.files {
|
||||||
println(q)
|
println(q)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// First pass (declarations)
|
// First pass (declarations)
|
||||||
@ -251,7 +251,7 @@ pub fn (v mut V) compile() {
|
|||||||
mut defs_pos := cgen.lines.len - 1
|
mut defs_pos := cgen.lines.len - 1
|
||||||
if defs_pos == -1 {
|
if defs_pos == -1 {
|
||||||
defs_pos = 0
|
defs_pos = 0
|
||||||
}
|
}
|
||||||
cgen.nogen = q
|
cgen.nogen = q
|
||||||
for file in v.files {
|
for file in v.files {
|
||||||
v.parse(file, .main)
|
v.parse(file, .main)
|
||||||
@ -272,7 +272,7 @@ pub fn (v mut V) compile() {
|
|||||||
v.vgen_buf.free()
|
v.vgen_buf.free()
|
||||||
vgen_parser.parse(.main)
|
vgen_parser.parse(.main)
|
||||||
// v.parsers.add(vgen_parser)
|
// v.parsers.add(vgen_parser)
|
||||||
|
|
||||||
// All definitions
|
// All definitions
|
||||||
mut def := strings.new_builder(10000)// Avoid unnecessary allocations
|
mut def := strings.new_builder(10000)// Avoid unnecessary allocations
|
||||||
$if !js {
|
$if !js {
|
||||||
@ -303,7 +303,7 @@ pub fn (v mut V) compile() {
|
|||||||
}
|
}
|
||||||
$if js {
|
$if js {
|
||||||
cgen.genln('main__main();')
|
cgen.genln('main__main();')
|
||||||
}
|
}
|
||||||
cgen.save()
|
cgen.save()
|
||||||
v.cc()
|
v.cc()
|
||||||
}
|
}
|
||||||
@ -420,13 +420,13 @@ pub fn (v mut V) generate_main() {
|
|||||||
}
|
}
|
||||||
// Generate a C `main`, which calls every single test function
|
// Generate a C `main`, which calls every single test function
|
||||||
v.gen_main_start(false)
|
v.gen_main_start(false)
|
||||||
|
|
||||||
if v.pref.is_stats { cgen.genln('BenchedTests bt = main__start_testing();') }
|
if v.pref.is_stats { cgen.genln('BenchedTests bt = main__start_testing();') }
|
||||||
|
|
||||||
for _, f in v.table.fns {
|
for _, f in v.table.fns {
|
||||||
if f.name.starts_with('main__test_') {
|
if f.name.starts_with('main__test_') {
|
||||||
if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_start(&bt, tos3("$f.name"));') }
|
if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_start(&bt, tos3("$f.name"));') }
|
||||||
cgen.genln('$f.name();')
|
cgen.genln('$f.name();')
|
||||||
if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_end(&bt);') }
|
if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_end(&bt);') }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -471,7 +471,7 @@ pub fn final_target_out_name(out_name string) string {
|
|||||||
pub fn (v V) run_compiled_executable_and_exit() {
|
pub fn (v V) run_compiled_executable_and_exit() {
|
||||||
if v.pref.is_verbose {
|
if v.pref.is_verbose {
|
||||||
println('============ running $v.out_name ============')
|
println('============ running $v.out_name ============')
|
||||||
}
|
}
|
||||||
mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe','') + '"'
|
mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe','') + '"'
|
||||||
if os.args.len > 3 {
|
if os.args.len > 3 {
|
||||||
cmd += ' ' + os.args.right(3).join(' ')
|
cmd += ' ' + os.args.right(3).join(' ')
|
||||||
@ -631,7 +631,7 @@ pub fn (v &V) get_user_files() []string {
|
|||||||
user_files << os.join(v.vroot, 'vlib', 'benchmark', 'tests',
|
user_files << os.join(v.vroot, 'vlib', 'benchmark', 'tests',
|
||||||
'always_imported.v')
|
'always_imported.v')
|
||||||
}
|
}
|
||||||
|
|
||||||
// v volt/slack_test.v: compile all .v files to get the environment
|
// v volt/slack_test.v: compile all .v files to get the environment
|
||||||
// I need to implement user packages! TODO
|
// I need to implement user packages! TODO
|
||||||
is_test_with_imports := dir.ends_with('_test.v') &&
|
is_test_with_imports := dir.ends_with('_test.v') &&
|
||||||
@ -754,14 +754,14 @@ pub fn new_v(args[]string) &V {
|
|||||||
os.mkdir(v_modules_path)
|
os.mkdir(v_modules_path)
|
||||||
os.mkdir('$v_modules_path${os.path_separator}cache')
|
os.mkdir('$v_modules_path${os.path_separator}cache')
|
||||||
}
|
}
|
||||||
|
|
||||||
mut vgen_buf := strings.new_builder(1000)
|
mut vgen_buf := strings.new_builder(1000)
|
||||||
vgen_buf.writeln('module main\nimport strings')
|
vgen_buf.writeln('module main\nimport strings')
|
||||||
|
|
||||||
joined_args := args.join(' ')
|
joined_args := args.join(' ')
|
||||||
target_os := get_arg(joined_args, 'os', '')
|
target_os := get_arg(joined_args, 'os', '')
|
||||||
mut out_name := get_arg(joined_args, 'o', 'a.out')
|
mut out_name := get_arg(joined_args, 'o', 'a.out')
|
||||||
|
|
||||||
mut dir := args.last()
|
mut dir := args.last()
|
||||||
if 'run' in args {
|
if 'run' in args {
|
||||||
dir = get_param_after(joined_args, 'run', '')
|
dir = get_param_after(joined_args, 'run', '')
|
||||||
@ -891,7 +891,7 @@ pub fn new_v(args[]string) &V {
|
|||||||
is_vlines: '-g' in args && !('-cg' in args)
|
is_vlines: '-g' in args && !('-cg' in args)
|
||||||
is_keep_c: '-keep_c' in args
|
is_keep_c: '-keep_c' in args
|
||||||
is_cache: '-cache' in args
|
is_cache: '-cache' in args
|
||||||
|
|
||||||
is_stats: '-stats' in args
|
is_stats: '-stats' in args
|
||||||
obfuscate: obfuscate
|
obfuscate: obfuscate
|
||||||
is_prof: '-prof' in args
|
is_prof: '-prof' in args
|
||||||
|
@ -59,9 +59,9 @@ mut:
|
|||||||
calling_c bool
|
calling_c bool
|
||||||
cur_fn Fn
|
cur_fn Fn
|
||||||
local_vars []Var // local function variables
|
local_vars []Var // local function variables
|
||||||
var_idx int
|
var_idx int
|
||||||
returns bool
|
returns bool
|
||||||
vroot string
|
vroot string
|
||||||
is_c_struct_init bool
|
is_c_struct_init bool
|
||||||
is_empty_c_struct_init bool
|
is_empty_c_struct_init bool
|
||||||
is_c_fn_call bool
|
is_c_fn_call bool
|
||||||
@ -71,7 +71,7 @@ mut:
|
|||||||
var_decl_name string // To allow declaring the variable so that it can be used in the struct initialization
|
var_decl_name string // To allow declaring the variable so that it can be used in the struct initialization
|
||||||
is_alloc bool // Whether current expression resulted in an allocation
|
is_alloc bool // Whether current expression resulted in an allocation
|
||||||
is_const_literal bool // `1`, `2.0` etc, so that `u64_var == 0` works
|
is_const_literal bool // `1`, `2.0` etc, so that `u64_var == 0` works
|
||||||
cur_gen_type string // "App" to replace "T" in current generic function
|
in_dispatch bool // dispatching generic instance?
|
||||||
is_vweb bool
|
is_vweb bool
|
||||||
is_sql bool
|
is_sql bool
|
||||||
is_js bool
|
is_js bool
|
||||||
@ -671,7 +671,11 @@ fn (p mut Parser) struct_decl() {
|
|||||||
}
|
}
|
||||||
mut typ := p.table.find_type(name)
|
mut typ := p.table.find_type(name)
|
||||||
if p.pass == .decl && p.table.known_type_fast(typ) {
|
if p.pass == .decl && p.table.known_type_fast(typ) {
|
||||||
p.error('`$name` redeclared')
|
if name in reserved_type_param_names {
|
||||||
|
p.error('name `$name` is reserved for type parameters')
|
||||||
|
} else {
|
||||||
|
p.error('type `$name` redeclared')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if is_objc {
|
if is_objc {
|
||||||
// Forward declaration of an Objective-C interface with `@class` :)
|
// Forward declaration of an Objective-C interface with `@class` :)
|
||||||
@ -1030,7 +1034,8 @@ fn (p mut Parser) get_type() string {
|
|||||||
p.register_map(typ)
|
p.register_map(typ)
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
//
|
|
||||||
|
// ptr/ref
|
||||||
mut warn := false
|
mut warn := false
|
||||||
for p.tok == .mul {
|
for p.tok == .mul {
|
||||||
if p.first_pass() {
|
if p.first_pass() {
|
||||||
@ -1045,7 +1050,16 @@ fn (p mut Parser) get_type() string {
|
|||||||
nr_muls++
|
nr_muls++
|
||||||
p.check(.amp)
|
p.check(.amp)
|
||||||
}
|
}
|
||||||
typ += p.lit
|
|
||||||
|
// Generic type check
|
||||||
|
ti := p.cur_fn.dispatch_of.inst
|
||||||
|
if p.lit in ti.keys() {
|
||||||
|
typ += ti[p.lit]
|
||||||
|
// println('cur dispatch: $p.lit => $typ')
|
||||||
|
} else {
|
||||||
|
typ += p.lit
|
||||||
|
}
|
||||||
|
|
||||||
if !p.is_struct_init {
|
if !p.is_struct_init {
|
||||||
// Otherwise we get `foo := FooFoo{` because `Foo` was already
|
// Otherwise we get `foo := FooFoo{` because `Foo` was already
|
||||||
// generated in name_expr()
|
// generated in name_expr()
|
||||||
@ -1164,8 +1178,7 @@ fn (p mut Parser) statements_no_rcbr() string {
|
|||||||
mut last_st_typ := ''
|
mut last_st_typ := ''
|
||||||
for p.tok != .rcbr && p.tok != .eof && p.tok != .key_case &&
|
for p.tok != .rcbr && p.tok != .eof && p.tok != .key_case &&
|
||||||
p.tok != .key_default && p.peek() != .arrow {
|
p.tok != .key_default && p.peek() != .arrow {
|
||||||
// println(p.tok.str())
|
// println('stm: '+p.tok.str()+', next: '+p.peek().str())
|
||||||
// p.print_tok()
|
|
||||||
last_st_typ = p.statement(true)
|
last_st_typ = p.statement(true)
|
||||||
// println('last st typ=$last_st_typ')
|
// println('last st typ=$last_st_typ')
|
||||||
if !p.inside_if_expr {
|
if !p.inside_if_expr {
|
||||||
@ -1186,7 +1199,6 @@ fn (p mut Parser) statements_no_rcbr() string {
|
|||||||
// p.check(.rcbr)
|
// p.check(.rcbr)
|
||||||
}
|
}
|
||||||
//p.fmt_dec()
|
//p.fmt_dec()
|
||||||
// println('close scope line=$p.scanner.line_nr')
|
|
||||||
|
|
||||||
p.close_scope()
|
p.close_scope()
|
||||||
return last_st_typ
|
return last_st_typ
|
||||||
@ -1255,7 +1267,9 @@ fn (p mut Parser) statement(add_semi bool) string {
|
|||||||
if p.returns && !p.is_vweb {
|
if p.returns && !p.is_vweb {
|
||||||
p.error('unreachable code')
|
p.error('unreachable code')
|
||||||
}
|
}
|
||||||
p.cgen.is_tmp = false
|
// if !p.in_dispatch {
|
||||||
|
p.cgen.is_tmp = false
|
||||||
|
// }
|
||||||
tok := p.tok
|
tok := p.tok
|
||||||
mut q := ''
|
mut q := ''
|
||||||
switch tok {
|
switch tok {
|
||||||
@ -1719,73 +1733,43 @@ fn (p mut Parser) name_expr() string {
|
|||||||
// known_type := p.table.known_type(name)
|
// known_type := p.table.known_type(name)
|
||||||
orig_name := name
|
orig_name := name
|
||||||
is_c := name == 'C' && p.peek() == .dot
|
is_c := name == 'C' && p.peek() == .dot
|
||||||
mut is_c_struct_init := is_c && ptr// a := &C.mycstruct{}
|
|
||||||
if is_c {
|
if is_c {
|
||||||
p.next()
|
p.check(.name)
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
name = p.lit
|
name = p.lit
|
||||||
p.fgen(name)
|
// C struct initialization
|
||||||
// Currently struct init is set to true only we have `&C.Foo{}`, handle `C.Foo{}`:
|
if p.peek() == .lcbr && p.table.known_type(name) {
|
||||||
if !is_c_struct_init && p.peek() == .lcbr {
|
return p.get_struct_type(name, true, ptr)
|
||||||
is_c_struct_init = true
|
|
||||||
}
|
}
|
||||||
|
// C function
|
||||||
|
if p.peek() == .lpar {
|
||||||
|
return p.get_c_func_type(name)
|
||||||
|
}
|
||||||
|
// C const (`C.GLFW_KEY_LEFT`)
|
||||||
|
p.gen(name)
|
||||||
|
p.next()
|
||||||
|
return 'int'
|
||||||
}
|
}
|
||||||
|
|
||||||
// enum value? (`color == .green`)
|
// enum value? (`color == .green`)
|
||||||
if p.tok == .dot {
|
if p.tok == .dot {
|
||||||
//println('got enum dot val $p.left_type pass=$p.pass $p.scanner.line_nr left=$p.left_type')
|
if p.table.known_type(p.expected_type) {
|
||||||
T := p.find_type(p.expected_type)
|
p.check_enum_member_access()
|
||||||
if T.cat == .enum_ {
|
// println("found enum value: $p.expected_type")
|
||||||
p.check(.dot)
|
return p.expected_type
|
||||||
val := p.check_name()
|
} else {
|
||||||
// Make sure this enum value exists
|
p.error("unknown enum: `$p.expected_type`")
|
||||||
if !T.has_enum_val(val) {
|
|
||||||
p.error('enum `$T.name` does not have value `$val`')
|
|
||||||
}
|
|
||||||
p.gen(mod_gen_name(T.mod) + '__' + p.expected_type + '_' + val)
|
|
||||||
}
|
}
|
||||||
return p.expected_type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variable, checked before modules, so module shadowing is allowed.
|
// Variable, checked before modules, so module shadowing is allowed.
|
||||||
// (`gg = gg.newcontext(); gg.draw_rect(...)`)
|
// (`gg = gg.newcontext(); gg.draw_rect(...)`)
|
||||||
for { // TODO remove
|
if p.known_var_check_new_var(name) {
|
||||||
mut v := p.find_var_check_new_var(name) or { break }
|
rtyp := p.get_var_type(name, ptr, deref)
|
||||||
if name == '_' {
|
return rtyp
|
||||||
p.error('cannot use `_` as value')
|
|
||||||
}
|
}
|
||||||
if ptr {
|
|
||||||
p.gen('&')
|
|
||||||
}
|
|
||||||
else if deref {
|
|
||||||
p.gen('*')
|
|
||||||
}
|
|
||||||
if p.pref.autofree && v.typ == 'string' && v.is_arg &&
|
|
||||||
p.assigned_type == 'string' {
|
|
||||||
p.warn('setting moved ' + v.typ)
|
|
||||||
p.mark_arg_moved(v)
|
|
||||||
}
|
|
||||||
mut typ := p.var_expr(v)
|
|
||||||
// *var
|
|
||||||
if deref {
|
|
||||||
if !typ.contains('*') && !typ.ends_with('ptr') {
|
|
||||||
println('name="$name", t=$v.typ')
|
|
||||||
p.error('dereferencing requires a pointer, but got `$typ`')
|
|
||||||
}
|
|
||||||
typ = typ.replace('ptr', '')// TODO
|
|
||||||
typ = typ.replace('*', '')// TODO
|
|
||||||
}
|
|
||||||
// &var
|
|
||||||
else if ptr {
|
|
||||||
typ += '*'
|
|
||||||
}
|
|
||||||
if p.inside_return_expr {
|
|
||||||
//println('marking $v.name returned')
|
|
||||||
p.mark_var_returned(v)
|
|
||||||
// v.is_returned = true // TODO modifying a local variable
|
|
||||||
// that's not used afterwards, this should be a compilation
|
|
||||||
// error
|
|
||||||
}
|
|
||||||
return typ
|
|
||||||
} // TODO REMOVE for{}
|
|
||||||
// Module?
|
// Module?
|
||||||
if p.peek() == .dot && ((name == p.mod && p.table.known_mod(name)) ||
|
if p.peek() == .dot && ((name == p.mod && p.table.known_mod(name)) ||
|
||||||
p.import_table.known_alias(name)) && !is_c {
|
p.import_table.known_alias(name)) && !is_c {
|
||||||
@ -1801,6 +1785,7 @@ fn (p mut Parser) name_expr() string {
|
|||||||
p.fgen(name)
|
p.fgen(name)
|
||||||
name = prepend_mod(mod_gen_name(mod), name)
|
name = prepend_mod(mod_gen_name(mod), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown name, try prepending the module name to it
|
// Unknown name, try prepending the module name to it
|
||||||
// TODO perf
|
// TODO perf
|
||||||
else if !p.table.known_type(name) &&
|
else if !p.table.known_type(name) &&
|
||||||
@ -1809,52 +1794,15 @@ fn (p mut Parser) name_expr() string {
|
|||||||
name = p.prepend_mod(name)
|
name = p.prepend_mod(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variable, checked before modules, so module shadowing is allowed.
|
// re-check
|
||||||
// (`gg = gg.newcontext(); gg.draw_rect(...)`)
|
if p.known_var_check_new_var(name) {
|
||||||
for { // TODO remove
|
return p.get_var_type(name, ptr, deref)
|
||||||
mut v := p.find_var_check_new_var(name) or { break }
|
|
||||||
if name == '_' {
|
|
||||||
p.error('cannot use `_` as value')
|
|
||||||
}
|
}
|
||||||
if ptr {
|
|
||||||
p.gen('&')
|
|
||||||
}
|
|
||||||
else if deref {
|
|
||||||
p.gen('*')
|
|
||||||
}
|
|
||||||
if p.pref.autofree && v.typ == 'string' && v.is_arg &&
|
|
||||||
p.assigned_type == 'string' {
|
|
||||||
p.warn('setting moved ' + v.typ)
|
|
||||||
p.mark_arg_moved(v)
|
|
||||||
}
|
|
||||||
mut typ := p.var_expr(v)
|
|
||||||
// *var
|
|
||||||
if deref {
|
|
||||||
if !typ.contains('*') && !typ.ends_with('ptr') {
|
|
||||||
println('name="$name", t=$v.typ')
|
|
||||||
p.error('dereferencing requires a pointer, but got `$typ`')
|
|
||||||
}
|
|
||||||
typ = typ.replace('ptr', '')// TODO
|
|
||||||
typ = typ.replace('*', '')// TODO
|
|
||||||
}
|
|
||||||
// &var
|
|
||||||
else if ptr {
|
|
||||||
typ += '*'
|
|
||||||
}
|
|
||||||
if p.inside_return_expr {
|
|
||||||
//println('marking $v.name returned')
|
|
||||||
p.mark_var_returned(v)
|
|
||||||
// v.is_returned = true // TODO modifying a local variable
|
|
||||||
// that's not used afterwards, this should be a compilation
|
|
||||||
// error
|
|
||||||
}
|
|
||||||
return typ
|
|
||||||
} // TODO REMOVE for{}
|
|
||||||
|
|
||||||
// if known_type || is_c_struct_init || (p.first_pass() && p.peek() == .lcbr) {
|
// if known_type || is_c_struct_init || (p.first_pass() && p.peek() == .lcbr) {
|
||||||
// known type? int(4.5) or Color.green (enum)
|
// known type? int(4.5) or Color.green (enum)
|
||||||
if p.table.known_type(name) {
|
if p.table.known_type(name) {
|
||||||
// float(5), byte(0), (*int)(ptr) etc
|
// cast expression: float(5), byte(0), (*int)(ptr) etc
|
||||||
if !is_c && ( p.peek() == .lpar || (deref && p.peek() == .rpar) ) {
|
if !is_c && ( p.peek() == .lpar || (deref && p.peek() == .rpar) ) {
|
||||||
if deref {
|
if deref {
|
||||||
name += '*'
|
name += '*'
|
||||||
@ -1885,87 +1833,20 @@ fn (p mut Parser) name_expr() string {
|
|||||||
p.next()
|
p.next()
|
||||||
return enum_type.name
|
return enum_type.name
|
||||||
}
|
}
|
||||||
// struct initialization
|
// normal struct init (non-C)
|
||||||
else if p.peek() == .lcbr {
|
else if p.peek() == .lcbr {
|
||||||
if ptr {
|
return p.get_struct_type(name, false, ptr)
|
||||||
name += '*' // `&User{}` => type `User*`
|
|
||||||
}
|
|
||||||
if name == 'T' {
|
|
||||||
name = p.cur_gen_type
|
|
||||||
}
|
|
||||||
p.is_c_struct_init = is_c_struct_init
|
|
||||||
return p.struct_init(name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if is_c {
|
|
||||||
// C const (`C.GLFW_KEY_LEFT`)
|
|
||||||
if p.peek() != .lpar {
|
|
||||||
p.gen(name)
|
|
||||||
p.next()
|
|
||||||
return 'int'
|
|
||||||
}
|
|
||||||
// C function
|
|
||||||
f := Fn {
|
|
||||||
name: name
|
|
||||||
is_c: true
|
|
||||||
}
|
|
||||||
p.is_c_fn_call = true
|
|
||||||
p.fn_call(f, 0, '', '')
|
|
||||||
p.is_c_fn_call = false
|
|
||||||
// Try looking it up. Maybe its defined with "C.fn_name() fn_type",
|
|
||||||
// then we know what type it returns
|
|
||||||
cfn := p.table.find_fn(name) or {
|
|
||||||
// Not Found? Return 'void*'
|
|
||||||
//return 'cvoid' //'void*'
|
|
||||||
if false {
|
|
||||||
p.warn('\ndefine imported C function with ' +
|
|
||||||
'`fn C.$name([args]) [return_type]`\n')
|
|
||||||
}
|
|
||||||
return 'void*'
|
|
||||||
}
|
|
||||||
return cfn.typ
|
|
||||||
}
|
|
||||||
// Constant
|
// Constant
|
||||||
for {
|
if p.table.known_const(name) {
|
||||||
c := p.table.find_const(name) or { break }
|
return p.get_const_type(name, ptr)
|
||||||
if ptr && !c.is_global {
|
|
||||||
p.error('cannot take the address of constant `$c.name`')
|
|
||||||
} else if ptr && c.is_global {
|
|
||||||
// c.ptr = true
|
|
||||||
p.gen('& /*const*/ ')
|
|
||||||
}
|
|
||||||
mut typ := p.var_expr(c)
|
|
||||||
if ptr {
|
|
||||||
typ += '*'
|
|
||||||
}
|
|
||||||
return typ
|
|
||||||
}
|
}
|
||||||
// Function (not method btw, methods are handled in `dot()`)
|
|
||||||
|
// Function (not method btw, methods are handled in dot())
|
||||||
mut f := p.table.find_fn_is_script(name, p.v_script) or {
|
mut f := p.table.find_fn_is_script(name, p.v_script) or {
|
||||||
// We are in the second pass, that means this function was not defined,
|
return p.get_undefined_fn_type(name, orig_name)
|
||||||
// throw an error.
|
|
||||||
if !p.first_pass() {
|
|
||||||
// check for misspelled function / variable / module
|
|
||||||
suggested := p.identify_typo(name, p.import_table)
|
|
||||||
if suggested != '' {
|
|
||||||
p.error('undefined: `$name`. did you mean:$suggested')
|
|
||||||
}
|
|
||||||
// If orig_name is a mod, then printing undefined: `mod` tells us nothing
|
|
||||||
if p.table.known_mod(orig_name) || p.import_table.known_alias(orig_name) {
|
|
||||||
name = name.replace('__', '.')
|
|
||||||
p.error('undefined: `$name`')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
p.error('undefined: `$orig_name`')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.next()
|
|
||||||
// First pass, the function can be defined later.
|
|
||||||
// Only in const definitions? (since fn bodies are skipped
|
|
||||||
// in the first pass).
|
|
||||||
return 'void'
|
|
||||||
}
|
|
||||||
return 'void'
|
|
||||||
}
|
}
|
||||||
// no () after func, so func is an argument, just gen its name
|
// no () after func, so func is an argument, just gen its name
|
||||||
// TODO verify this and handle errors
|
// TODO verify this and handle errors
|
||||||
@ -1986,8 +1867,20 @@ fn (p mut Parser) name_expr() string {
|
|||||||
if f.typ == 'void' && !p.inside_if_expr {
|
if f.typ == 'void' && !p.inside_if_expr {
|
||||||
// p.error('`$f.name` used as value')
|
// p.error('`$f.name` used as value')
|
||||||
}
|
}
|
||||||
//p.log('calling function')
|
|
||||||
p.fn_call(f, 0, '', '')
|
// println('call to fn $f.name of type $f.typ')
|
||||||
|
// TODO replace the following dirty hacks (needs ptr access to fn table)
|
||||||
|
new_f := f
|
||||||
|
p.fn_call(mut new_f, 0, '', '')
|
||||||
|
if f.is_generic {
|
||||||
|
f2 := p.table.find_fn(f.name) or {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
// println('after call of generic instance $new_f.name(${new_f.str_args(p.table)}) $new_f.typ')
|
||||||
|
// println(' from $f2.name(${f2.str_args(p.table)}) $f2.typ : $f2.type_inst')
|
||||||
|
}
|
||||||
|
f = new_f
|
||||||
|
|
||||||
// dot after a function call: `get_user().age`
|
// dot after a function call: `get_user().age`
|
||||||
if p.tok == .dot {
|
if p.tok == .dot {
|
||||||
mut typ := ''
|
mut typ := ''
|
||||||
@ -2005,6 +1898,146 @@ fn (p mut Parser) name_expr() string {
|
|||||||
return f.typ
|
return f.typ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) get_struct_type(name_ string, is_c bool, is_ptr bool) string {
|
||||||
|
mut name := name_
|
||||||
|
if is_ptr {
|
||||||
|
name += '*' // `&User{}` => type `User*`
|
||||||
|
}
|
||||||
|
if name in reserved_type_param_names {
|
||||||
|
p.warn('name `$name` is reserved for type parameters')
|
||||||
|
}
|
||||||
|
p.is_c_struct_init = is_c
|
||||||
|
return p.struct_init(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) check_enum_member_access() {
|
||||||
|
T := p.find_type(p.expected_type)
|
||||||
|
if T.cat == .enum_ {
|
||||||
|
p.check(.dot)
|
||||||
|
val := p.check_name()
|
||||||
|
// Make sure this enum value exists
|
||||||
|
if !T.has_enum_val(val) {
|
||||||
|
p.error('enum `$T.name` does not have value `$val`')
|
||||||
|
}
|
||||||
|
p.gen(mod_gen_name(T.mod) + '__' + p.expected_type + '_' + val)
|
||||||
|
} else {
|
||||||
|
p.error('`$T.name` is not an enum')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) get_var_type(name string, is_ptr bool, is_deref bool) string {
|
||||||
|
v := p.find_var_check_new_var(name) or { return "" }
|
||||||
|
if name == '_' {
|
||||||
|
p.error('cannot use `_` as value')
|
||||||
|
}
|
||||||
|
if is_ptr {
|
||||||
|
p.gen('&')
|
||||||
|
}
|
||||||
|
else if is_deref {
|
||||||
|
p.gen('*')
|
||||||
|
}
|
||||||
|
if p.pref.autofree && v.typ == 'string' && v.is_arg &&
|
||||||
|
p.assigned_type == 'string' {
|
||||||
|
p.warn('setting moved ' + v.typ)
|
||||||
|
p.mark_arg_moved(v)
|
||||||
|
}
|
||||||
|
mut typ := p.var_expr(v)
|
||||||
|
// *var
|
||||||
|
if is_deref {
|
||||||
|
if !typ.contains('*') && !typ.ends_with('ptr') {
|
||||||
|
println('name="$name", t=$v.typ')
|
||||||
|
p.error('dereferencing requires a pointer, but got `$typ`')
|
||||||
|
}
|
||||||
|
typ = typ.replace('ptr', '')// TODO
|
||||||
|
typ = typ.replace('*', '')// TODO
|
||||||
|
}
|
||||||
|
// &var
|
||||||
|
else if is_ptr {
|
||||||
|
typ += '*'
|
||||||
|
}
|
||||||
|
if p.inside_return_expr {
|
||||||
|
//println('marking $v.name returned')
|
||||||
|
p.mark_var_returned(v)
|
||||||
|
// v.is_returned = true // TODO modifying a local variable
|
||||||
|
// that's not used afterwards, this should be a compilation
|
||||||
|
// error
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) get_const_type(name string, is_ptr bool) string {
|
||||||
|
c := p.table.find_const(name) or { return "" }
|
||||||
|
if is_ptr && !c.is_global {
|
||||||
|
p.error('cannot take the address of constant `$c.name`')
|
||||||
|
} else if is_ptr && c.is_global {
|
||||||
|
// c.ptr = true
|
||||||
|
p.gen('& /*const*/ ')
|
||||||
|
}
|
||||||
|
mut typ := p.var_expr(c)
|
||||||
|
if is_ptr {
|
||||||
|
typ += '*'
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) get_c_func_type(name string) string {
|
||||||
|
f := Fn {
|
||||||
|
name: name
|
||||||
|
is_c: true
|
||||||
|
}
|
||||||
|
p.is_c_fn_call = true
|
||||||
|
p.fn_call(mut f, 0, '', '')
|
||||||
|
p.is_c_fn_call = false
|
||||||
|
// Try looking it up. Maybe its defined with "C.fn_name() fn_type",
|
||||||
|
// then we know what type it returns
|
||||||
|
cfn := p.table.find_fn(name) or {
|
||||||
|
// Not Found? Return 'void*'
|
||||||
|
//return 'cvoid' //'void*'
|
||||||
|
if false {
|
||||||
|
p.warn('\ndefine imported C function with ' +
|
||||||
|
'`fn C.$name([args]) [return_type]`\n')
|
||||||
|
}
|
||||||
|
return 'void*'
|
||||||
|
}
|
||||||
|
// println("C fn $name has type $cfn.typ")
|
||||||
|
return cfn.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) get_undefined_fn_type(name string, orig_name string) string {
|
||||||
|
if p.first_pass() {
|
||||||
|
p.next()
|
||||||
|
// First pass, the function can be defined later.
|
||||||
|
return 'void'
|
||||||
|
} else {
|
||||||
|
// We are in the second pass, that means this function was not defined, throw an error.
|
||||||
|
|
||||||
|
// V script? Try os module.
|
||||||
|
// TODO
|
||||||
|
if p.v_script {
|
||||||
|
//name = name.replace('main__', 'os__')
|
||||||
|
//f = p.table.find_fn(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for misspelled function / variable / module
|
||||||
|
suggested := p.identify_typo(name, p.import_table)
|
||||||
|
if suggested != '' {
|
||||||
|
p.error('undefined function: `$name`. did you mean: `$suggested`')
|
||||||
|
}
|
||||||
|
|
||||||
|
// If orig_name is a mod, then printing undefined: `mod` tells us nothing
|
||||||
|
// if p.table.known_mod(orig_name) {
|
||||||
|
if p.table.known_mod(orig_name) || p.import_table.known_alias(orig_name) {
|
||||||
|
m_name := mod_gen_name_rev(name.replace('__', '.'))
|
||||||
|
p.error('undefined function: `$m_name` (in module `$orig_name`)')
|
||||||
|
} else if orig_name in reserved_type_param_names {
|
||||||
|
p.error('the letter `$orig_name` is reserved for type parameters')
|
||||||
|
} else {
|
||||||
|
p.error('undefined symbol: `$orig_name`')
|
||||||
|
}
|
||||||
|
return 'void'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (p mut Parser) var_expr(v Var) string {
|
fn (p mut Parser) var_expr(v Var) string {
|
||||||
//p.log('\nvar_expr() v.name="$v.name" v.typ="$v.typ"')
|
//p.log('\nvar_expr() v.name="$v.name" v.typ="$v.typ"')
|
||||||
// println('var expr is_tmp=$p.cgen.is_tmp\n')
|
// println('var expr is_tmp=$p.cgen.is_tmp\n')
|
||||||
@ -2088,11 +2121,6 @@ fn (p mut Parser) var_expr(v Var) string {
|
|||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
// for debugging only
|
|
||||||
fn (p &Parser) fileis(s string) bool {
|
|
||||||
return p.scanner.file_path.contains(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// user.name => `str_typ` is `User`
|
// user.name => `str_typ` is `User`
|
||||||
// user.company.name => `str_typ` is `Company`
|
// user.company.name => `str_typ` is `Company`
|
||||||
fn (p mut Parser) dot(str_typ_ string, method_ph int) string {
|
fn (p mut Parser) dot(str_typ_ string, method_ph int) string {
|
||||||
@ -2203,7 +2231,7 @@ struct $typ.name {
|
|||||||
p.error_with_token_index('could not find method `$field_name`', fname_tidx) // should never happen
|
p.error_with_token_index('could not find method `$field_name`', fname_tidx) // should never happen
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
p.fn_call(method, method_ph, '', str_typ)
|
p.fn_call(mut method, method_ph, '', str_typ)
|
||||||
// Methods returning `array` should return `array_string`
|
// Methods returning `array` should return `array_string`
|
||||||
if method.typ == 'array' && typ.name.starts_with('array_') {
|
if method.typ == 'array' && typ.name.starts_with('array_') {
|
||||||
return typ.name
|
return typ.name
|
||||||
@ -2404,6 +2432,11 @@ struct IndexCfg {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for debugging only
|
||||||
|
fn (p &Parser) fileis(s string) bool {
|
||||||
|
return p.scanner.file_path.contains(s)
|
||||||
|
}
|
||||||
|
|
||||||
// in and dot have higher priority than `!`
|
// in and dot have higher priority than `!`
|
||||||
fn (p mut Parser) indot_expr() string {
|
fn (p mut Parser) indot_expr() string {
|
||||||
ph := p.cgen.add_placeholder()
|
ph := p.cgen.add_placeholder()
|
||||||
@ -3905,6 +3938,7 @@ fn (p mut Parser) return_st() {
|
|||||||
is_none := p.tok == .key_none
|
is_none := p.tok == .key_none
|
||||||
p.expected_type = p.cur_fn.typ
|
p.expected_type = p.cur_fn.typ
|
||||||
mut expr_type := p.bool_expression()
|
mut expr_type := p.bool_expression()
|
||||||
|
// println('$p.cur_fn.name returns type $expr_type, should be $p.cur_fn.typ')
|
||||||
mut types := []string
|
mut types := []string
|
||||||
mut mr_values := [p.cgen.cur_line.right(ph).trim_space()]
|
mut mr_values := [p.cgen.cur_line.right(ph).trim_space()]
|
||||||
types << expr_type
|
types << expr_type
|
||||||
|
@ -12,7 +12,6 @@ mut:
|
|||||||
typesmap map[string]Type
|
typesmap map[string]Type
|
||||||
consts []Var
|
consts []Var
|
||||||
fns map[string]Fn
|
fns map[string]Fn
|
||||||
generic_fns []GenTable //map[string]GenTable // generic_fns['listen_and_serve'] == ['Blog', 'Forum']
|
|
||||||
obf_ids map[string]int // obf_ids['myfunction'] == 23
|
obf_ids map[string]int // obf_ids['myfunction'] == 23
|
||||||
modules []string // List of all modules registered by the application
|
modules []string // List of all modules registered by the application
|
||||||
imports []string // List of all imports
|
imports []string // List of all imports
|
||||||
@ -40,12 +39,6 @@ enum NameCategory {
|
|||||||
struct Name {
|
struct Name {
|
||||||
cat NameCategory
|
cat NameCategory
|
||||||
idx int // e.g. typ := types[name.idx]
|
idx int // e.g. typ := types[name.idx]
|
||||||
}
|
|
||||||
|
|
||||||
struct GenTable {
|
|
||||||
fn_name string
|
|
||||||
mut:
|
|
||||||
types []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Holds import information scoped to the parsed file
|
// Holds import information scoped to the parsed file
|
||||||
@ -125,7 +118,7 @@ mut:
|
|||||||
// This information is needed in the first pass.
|
// This information is needed in the first pass.
|
||||||
is_placeholder bool
|
is_placeholder bool
|
||||||
gen_str bool // needs `.str()` method generation
|
gen_str bool // needs `.str()` method generation
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypeNode {
|
struct TypeNode {
|
||||||
@ -218,6 +211,7 @@ fn (t &Table) debug_fns() string {
|
|||||||
const (
|
const (
|
||||||
number_types = ['number', 'int', 'i8', 'i16', 'u16', 'u32', 'byte', 'i64', 'u64', 'f32', 'f64']
|
number_types = ['number', 'int', 'i8', 'i16', 'u16', 'u32', 'byte', 'i64', 'u64', 'f32', 'f64']
|
||||||
float_types = ['f32', 'f64']
|
float_types = ['f32', 'f64']
|
||||||
|
reserved_type_param_names = ['R', 'S', 'T', 'U', 'W']
|
||||||
)
|
)
|
||||||
|
|
||||||
fn is_number_type(typ string) bool {
|
fn is_number_type(typ string) bool {
|
||||||
@ -254,8 +248,10 @@ fn new_table(obfuscate bool) &Table {
|
|||||||
t.register_type('bool')
|
t.register_type('bool')
|
||||||
t.register_type('void')
|
t.register_type('void')
|
||||||
t.register_type('voidptr')
|
t.register_type('voidptr')
|
||||||
t.register_type('T')
|
|
||||||
t.register_type('va_list')
|
t.register_type('va_list')
|
||||||
|
for c in reserved_type_param_names {
|
||||||
|
t.register_type(c)
|
||||||
|
}
|
||||||
t.register_const('stdin', 'int', 'main')
|
t.register_const('stdin', 'int', 'main')
|
||||||
t.register_const('stdout', 'int', 'main')
|
t.register_const('stdout', 'int', 'main')
|
||||||
t.register_const('stderr', 'int', 'main')
|
t.register_const('stderr', 'int', 'main')
|
||||||
@ -585,6 +581,13 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
|||||||
if p.pref.translated {
|
if p.pref.translated {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generic return type
|
||||||
|
if expected == '_ANYTYPE_' {
|
||||||
|
p.cur_fn.typ = got
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// variadic
|
// variadic
|
||||||
if expected.starts_with('...') {
|
if expected.starts_with('...') {
|
||||||
expected = expected.right(3)
|
expected = expected.right(3)
|
||||||
@ -665,7 +668,7 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
|||||||
if got.starts_with('fn ') && (expected.ends_with('fn') ||
|
if got.starts_with('fn ') && (expected.ends_with('fn') ||
|
||||||
expected.ends_with('Fn')) {
|
expected.ends_with('Fn')) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Allow pointer arithmetic
|
// Allow pointer arithmetic
|
||||||
if expected=='void*' && got=='int' {
|
if expected=='void*' && got=='int' {
|
||||||
return true
|
return true
|
||||||
@ -676,7 +679,7 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
|||||||
//}
|
//}
|
||||||
if is_number_type(got) && is_number_type(expected) && p.is_const_literal {
|
if is_number_type(got) && is_number_type(expected) && p.is_const_literal {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
expected = expected.replace('*', '')
|
expected = expected.replace('*', '')
|
||||||
got = got.replace('*', '')
|
got = got.replace('*', '')
|
||||||
if got != expected {
|
if got != expected {
|
||||||
@ -749,7 +752,7 @@ fn (t &Table) has_at_least_one_test_fn() bool {
|
|||||||
for _, f in t.fns {
|
for _, f in t.fns {
|
||||||
if f.name.starts_with('main__test_') {
|
if f.name.starts_with('main__test_') {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -778,7 +781,7 @@ fn (table &Table) cgen_name_type_pair(name, typ string) string {
|
|||||||
else if typ.starts_with('fn (') {
|
else if typ.starts_with('fn (') {
|
||||||
T := table.find_type(typ)
|
T := table.find_type(typ)
|
||||||
if T.name == '' {
|
if T.name == '' {
|
||||||
println('this should never happen')
|
eprintln('function type `$typ` not found')
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
str_args := T.func.str_args(table)
|
str_args := T.func.str_args(table)
|
||||||
@ -811,32 +814,6 @@ fn is_valid_int_const(val, typ string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (t mut Table) register_generic_fn(fn_name string) {
|
|
||||||
t.generic_fns << GenTable{fn_name, []string}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (t &Table) fn_gen_types(fn_name string) []string {
|
|
||||||
for _, f in t.generic_fns {
|
|
||||||
if f.fn_name == fn_name {
|
|
||||||
return f.types
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verror('function $fn_name not found')
|
|
||||||
return []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// `foo<Bar>()`
|
|
||||||
// fn_name == 'foo'
|
|
||||||
// typ == 'Bar'
|
|
||||||
fn (t mut Table) register_generic_fn_type(fn_name, typ string) {
|
|
||||||
for i, f in t.generic_fns {
|
|
||||||
if f.fn_name == fn_name {
|
|
||||||
t.generic_fns[i].types << typ
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
||||||
t := p.table.find_type(typ)
|
t := p.table.find_type(typ)
|
||||||
if t.cat == .enum_ {
|
if t.cat == .enum_ {
|
||||||
@ -866,6 +843,11 @@ fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_to_safe_str(typ string) string {
|
||||||
|
r := typ.replace(' ','').replace('(','_').replace(')','_')
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
fn is_compile_time_const(s_ string) bool {
|
fn is_compile_time_const(s_ string) bool {
|
||||||
s := s_.trim_space()
|
s := s_.trim_space()
|
||||||
if s == '' {
|
if s == '' {
|
||||||
|
58
vlib/compiler/tests/generic_test.v
Normal file
58
vlib/compiler/tests/generic_test.v
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
fn simple<T>(p T) T {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum<T>(l []T, nil T) T {
|
||||||
|
mut r := nil
|
||||||
|
for e in l {
|
||||||
|
r += e
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_f<T,U,R>(l []T, f fn(T)U) []U {
|
||||||
|
mut r := []U
|
||||||
|
for e in l {
|
||||||
|
r << f(e)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foldl<T>(l []T, nil T, f fn(T,T)T) T {
|
||||||
|
mut r := nil
|
||||||
|
for e in l {
|
||||||
|
r = f(r, e)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn plus<T>(a T, b T) T {
|
||||||
|
return a+b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(x int) int {
|
||||||
|
return x*x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_int(x int, y int) int {
|
||||||
|
return x*y
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_eq<T>(a, b T) {
|
||||||
|
r := a == b
|
||||||
|
println('$a == $b: ${r.str()}')
|
||||||
|
assert r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_fn() {
|
||||||
|
assert_eq(simple(0+1), 1)
|
||||||
|
assert_eq(simple('g') + 'h', 'gh')
|
||||||
|
assert_eq(sum([5.1,6.2,7.0], 0.0), 18.3)
|
||||||
|
assert_eq(plus(i64(4), i64(6)), i64(10))
|
||||||
|
a := [1,2,3,4]
|
||||||
|
$if !windows {
|
||||||
|
b := map_f(a, square)
|
||||||
|
assert_eq(sum(b, 0), 30) // 1+4+9+16 = 30
|
||||||
|
assert_eq(foldl(b, 1, mul_int), 576) // 1*4*9*16 = 576
|
||||||
|
}
|
||||||
|
}
|
3
vlib/compiler/tests/print_test.v
Normal file
3
vlib/compiler/tests/print_test.v
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn test_print() {
|
||||||
|
println(2.0)
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
println(a)
|
println(a)
|
||||||
===output===
|
===output===
|
||||||
.vrepl.v:2:9: undefined: `a`
|
.vrepl.v:2:9: undefined symbol: `a`
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
a
|
a
|
||||||
33
|
33
|
||||||
===output===
|
===output===
|
||||||
.vrepl_temp.v:3:9: undefined: `a`
|
.vrepl_temp.v:3:9: undefined symbol: `a`
|
||||||
33
|
33
|
||||||
|
Loading…
Reference in New Issue
Block a user