all: generic functions
parent
b8c028c727
commit
ce1a181699
|
@ -226,6 +226,7 @@ pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
body_pos token.Position
|
body_pos token.Position
|
||||||
file string
|
file string
|
||||||
|
is_generic bool
|
||||||
pub mut:
|
pub mut:
|
||||||
return_type table.Type
|
return_type table.Type
|
||||||
}
|
}
|
||||||
|
@ -251,6 +252,7 @@ pub mut:
|
||||||
receiver_type table.Type // User
|
receiver_type table.Type // User
|
||||||
return_type table.Type
|
return_type table.Type
|
||||||
should_be_skipped bool
|
should_be_skipped bool
|
||||||
|
generic_type table.Type // TODO array, to support multiple types
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CallArg {
|
pub struct CallArg {
|
||||||
|
|
|
@ -37,7 +37,11 @@ pub fn (node &FnDecl) str(t &table.Table) string {
|
||||||
else if node.language == .js {
|
else if node.language == .js {
|
||||||
name = 'JS.$name'
|
name = 'JS.$name'
|
||||||
}
|
}
|
||||||
f.write('fn ${receiver}${name}(')
|
f.write('fn ${receiver}${name}')
|
||||||
|
if node.is_generic {
|
||||||
|
f.write('<T>')
|
||||||
|
}
|
||||||
|
f.write('(')
|
||||||
for i, arg in node.args {
|
for i, arg in node.args {
|
||||||
// skip receiver
|
// skip receiver
|
||||||
// if (node.is_method || node.is_interface) && i == 0 {
|
// if (node.is_method || node.is_interface) && i == 0 {
|
||||||
|
|
|
@ -320,7 +320,6 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
||||||
} else {
|
} else {
|
||||||
info = type_sym.info as table.Struct
|
info = type_sym.info as table.Struct
|
||||||
}
|
}
|
||||||
|
|
||||||
if struct_init.is_short && struct_init.fields.len > info.fields.len {
|
if struct_init.is_short && struct_init.fields.len > info.fields.len {
|
||||||
c.error('too many fields', struct_init.pos)
|
c.error('too many fields', struct_init.pos)
|
||||||
}
|
}
|
||||||
|
@ -889,6 +888,13 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
c.error('unknown function: $fn_name', call_expr.pos)
|
c.error('unknown function: $fn_name', call_expr.pos)
|
||||||
return table.void_type
|
return table.void_type
|
||||||
}
|
}
|
||||||
|
if call_expr.generic_type != table.void_type && f.return_type != 0 { // table.t_type {
|
||||||
|
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
||||||
|
sym := c.table.get_type_symbol(f.return_type)
|
||||||
|
if sym.name == 'T' {
|
||||||
|
return call_expr.generic_type
|
||||||
|
}
|
||||||
|
}
|
||||||
if !found_in_args && call_expr.mod in ['builtin', 'main'] {
|
if !found_in_args && call_expr.mod in ['builtin', 'main'] {
|
||||||
scope := c.file.scope.innermost(call_expr.pos.pos)
|
scope := c.file.scope.innermost(call_expr.pos.pos)
|
||||||
if _ := scope.find_var(fn_name) {
|
if _ := scope.find_var(fn_name) {
|
||||||
|
@ -896,8 +902,8 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
call_expr.pos)
|
call_expr.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !f.is_pub && f.language == .v && !c.is_builtin_mod && !c.pref.is_test && f.mod != c.mod && f.name !=
|
if !f.is_pub && f.language == .v && !c.is_builtin_mod && !c.pref.is_test && f.mod != c.mod &&
|
||||||
'' && f.mod != '' {
|
f.name != '' && f.mod != '' {
|
||||||
c.warn('function `$f.name` is private. curmod=$c.mod fmod=$f.mod', call_expr.pos)
|
c.warn('function `$f.name` is private. curmod=$c.mod fmod=$f.mod', call_expr.pos)
|
||||||
}
|
}
|
||||||
call_expr.return_type = f.return_type
|
call_expr.return_type = f.return_type
|
||||||
|
@ -1536,7 +1542,8 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
mut scope := c.file.scope.innermost(it.pos.pos)
|
mut scope := c.file.scope.innermost(it.pos.pos)
|
||||||
sym := c.table.get_type_symbol(typ)
|
sym := c.table.get_type_symbol(typ)
|
||||||
if sym.kind == .map && !(it.key_var.len > 0 && it.val_var.len > 0) {
|
if sym.kind == .map && !(it.key_var.len > 0 && it.val_var.len > 0) {
|
||||||
c.error('for in: cannot use one variable in map', it.pos)
|
c.error('declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
|
||||||
|
'use `_` if you do not need the variable', it.pos)
|
||||||
}
|
}
|
||||||
if it.key_var.len > 0 {
|
if it.key_var.len > 0 {
|
||||||
key_type := match sym.kind {
|
key_type := match sym.kind {
|
||||||
|
|
|
@ -95,6 +95,7 @@ mut:
|
||||||
is_builtin_mod bool
|
is_builtin_mod bool
|
||||||
hotcode_fn_names []string
|
hotcode_fn_names []string
|
||||||
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
||||||
|
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -290,6 +291,10 @@ pub fn (mut g Gen) write_typeof_functions() {
|
||||||
// V type to C type
|
// V type to C type
|
||||||
fn (mut g Gen) typ(t table.Type) string {
|
fn (mut g Gen) typ(t table.Type) string {
|
||||||
mut styp := g.base_type(t)
|
mut styp := g.base_type(t)
|
||||||
|
if styp.len == 1 && g.cur_generic_type != 0 {
|
||||||
|
// T => int etc
|
||||||
|
return g.typ(g.cur_generic_type)
|
||||||
|
}
|
||||||
base := styp
|
base := styp
|
||||||
if t.flag_is(.optional) {
|
if t.flag_is(.optional) {
|
||||||
if t.is_ptr() {
|
if t.is_ptr() {
|
||||||
|
@ -365,12 +370,10 @@ typedef struct {
|
||||||
.alias {
|
.alias {
|
||||||
parent := &g.table.types[typ.parent_idx]
|
parent := &g.table.types[typ.parent_idx]
|
||||||
styp := typ.name.replace('.', '__')
|
styp := typ.name.replace('.', '__')
|
||||||
is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] == `.`
|
is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] ==
|
||||||
parent_styp := if is_c_parent {
|
`.`
|
||||||
'struct ' + parent.name[2..].replace('.', '__')
|
parent_styp := if is_c_parent { 'struct ' + parent.name[2..].replace('.', '__') } else { parent.name.replace('.',
|
||||||
} else {
|
'__') }
|
||||||
parent.name.replace('.', '__')
|
|
||||||
}
|
|
||||||
g.definitions.writeln('typedef $parent_styp $styp;')
|
g.definitions.writeln('typedef $parent_styp $styp;')
|
||||||
}
|
}
|
||||||
.array {
|
.array {
|
||||||
|
@ -1712,8 +1715,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
||||||
g.expr(node.right)
|
g.expr(node.right)
|
||||||
g.write(')')
|
g.write(')')
|
||||||
} else if node.op in [.plus, .minus, .mul, .div, .mod] && (left_sym.name[0].is_capital() ||
|
} else if node.op in [.plus, .minus, .mul, .div, .mod] && (left_sym.name[0].is_capital() ||
|
||||||
left_sym.name.contains('.')) && left_sym.kind != .alias ||
|
left_sym.name.contains('.')) && left_sym.kind != .alias || left_sym.kind == .alias && (left_sym.info as table.Alias).language ==
|
||||||
left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c {
|
.c {
|
||||||
// !left_sym.is_number() {
|
// !left_sym.is_number() {
|
||||||
g.write(g.typ(node.left_type))
|
g.write(g.typ(node.left_type))
|
||||||
g.write('_')
|
g.write('_')
|
||||||
|
@ -2258,7 +2261,9 @@ fn (mut g Gen) go_back_out(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
||||||
skip_init := ['strconv__ftoa__Uf32', 'strconv__ftoa__Uf64', 'strconv__Float64u', 'struct stat', 'struct addrinfo']
|
skip_init := ['strconv__ftoa__Uf32', 'strconv__ftoa__Uf64', 'strconv__Float64u', 'struct stat',
|
||||||
|
'struct addrinfo'
|
||||||
|
]
|
||||||
styp := g.typ(struct_init.typ)
|
styp := g.typ(struct_init.typ)
|
||||||
if styp in skip_init {
|
if styp in skip_init {
|
||||||
g.go_back_out(3)
|
g.go_back_out(3)
|
||||||
|
@ -2274,7 +2279,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
||||||
g.writeln('($styp){')
|
g.writeln('($styp){')
|
||||||
}
|
}
|
||||||
// mut fields := []string{}
|
// mut fields := []string{}
|
||||||
mut inited_fields := map[string]int // TODO this is done in checker, move to ast node
|
mut inited_fields := map[string]int{} // TODO this is done in checker, move to ast node
|
||||||
/*
|
/*
|
||||||
if struct_init.fields.len == 0 && struct_init.exprs.len > 0 {
|
if struct_init.fields.len == 0 && struct_init.exprs.len > 0 {
|
||||||
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
|
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
|
||||||
|
@ -2378,7 +2383,7 @@ fn (mut g Gen) assoc(node ast.Assoc) {
|
||||||
}
|
}
|
||||||
styp := g.typ(node.typ)
|
styp := g.typ(node.typ)
|
||||||
g.writeln('($styp){')
|
g.writeln('($styp){')
|
||||||
mut inited_fields := map[string]int
|
mut inited_fields := map[string]int{}
|
||||||
for i, field in node.fields {
|
for i, field in node.fields {
|
||||||
inited_fields[field] = i
|
inited_fields[field] = i
|
||||||
}
|
}
|
||||||
|
@ -2863,7 +2868,9 @@ fn (mut g Gen) gen_map(node ast.CallExpr) {
|
||||||
g.expr(node.args[0].expr)
|
g.expr(node.args[0].expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { g.expr(node.args[0].expr) }
|
else {
|
||||||
|
g.expr(node.args[0].expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.writeln(';')
|
g.writeln(';')
|
||||||
g.writeln('\tarray_push(&$tmp, &ti);')
|
g.writeln('\tarray_push(&$tmp, &ti);')
|
||||||
|
@ -2901,7 +2908,9 @@ fn (mut g Gen) gen_filter(node ast.CallExpr) {
|
||||||
g.expr(node.args[0].expr)
|
g.expr(node.args[0].expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { g.expr(node.args[0].expr) }
|
else {
|
||||||
|
g.expr(node.args[0].expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.writeln(') array_push(&$tmp, &it); \n }')
|
g.writeln(') array_push(&$tmp, &it); \n }')
|
||||||
g.write(s)
|
g.write(s)
|
||||||
|
@ -3815,7 +3824,7 @@ fn (g &Gen) interface_table() string {
|
||||||
mut methods_struct_def := strings.new_builder(100)
|
mut methods_struct_def := strings.new_builder(100)
|
||||||
methods_struct_def.writeln('$methods_struct_name {')
|
methods_struct_def.writeln('$methods_struct_name {')
|
||||||
mut imethods := map[string]string{} // a map from speak -> _Speaker_speak_fn
|
mut imethods := map[string]string{} // a map from speak -> _Speaker_speak_fn
|
||||||
mut methodidx := map[string]int
|
mut methodidx := map[string]int{}
|
||||||
for k, method in ityp.methods {
|
for k, method in ityp.methods {
|
||||||
methodidx[method.name] = k
|
methodidx[method.name] = k
|
||||||
typ_name := '_${interface_name}_${method.name}_fn'
|
typ_name := '_${interface_name}_${method.name}_fn'
|
||||||
|
|
|
@ -13,6 +13,16 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
is_main := it.name == 'main'
|
is_main := it.name == 'main'
|
||||||
|
if it.is_generic && g.cur_generic_type == 0 { // need the cur_generic_type check to avoid inf. recursion
|
||||||
|
// loop thru each generic type and generate a function
|
||||||
|
for gen_type in g.table.fn_gen_types[it.name] {
|
||||||
|
g.cur_generic_type = gen_type
|
||||||
|
g.gen_fn_decl(it)
|
||||||
|
println(gen_type)
|
||||||
|
}
|
||||||
|
g.cur_generic_type = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
//
|
//
|
||||||
if is_main && g.pref.is_liveshared {
|
if is_main && g.pref.is_liveshared {
|
||||||
return
|
return
|
||||||
|
@ -61,6 +71,10 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
||||||
} else {
|
} else {
|
||||||
name = c_name(name)
|
name = c_name(name)
|
||||||
}
|
}
|
||||||
|
if g.cur_generic_type != 0 {
|
||||||
|
// foo<T>() => foo_int(), foo_string() etc
|
||||||
|
name += '_' + g.typ(g.cur_generic_type)
|
||||||
|
}
|
||||||
// if g.pref.show_cc && it.is_builtin {
|
// if g.pref.show_cc && it.is_builtin {
|
||||||
// println(name)
|
// println(name)
|
||||||
// }
|
// }
|
||||||
|
@ -293,7 +307,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||||
verror('method receiver type is 0, this means there are some uchecked exprs')
|
verror('method receiver type is 0, this means there are some uchecked exprs')
|
||||||
}
|
}
|
||||||
// mut receiver_type_name := g.cc_type(node.receiver_type)
|
// mut receiver_type_name := g.cc_type(node.receiver_type)
|
||||||
//mut receiver_type_name := g.typ(node.receiver_type)
|
// mut receiver_type_name := g.typ(node.receiver_type)
|
||||||
typ_sym := g.table.get_type_symbol(node.receiver_type)
|
typ_sym := g.table.get_type_symbol(node.receiver_type)
|
||||||
mut receiver_type_name := typ_sym.name.replace('.', '__')
|
mut receiver_type_name := typ_sym.name.replace('.', '__')
|
||||||
if typ_sym.kind == .interface_ {
|
if typ_sym.kind == .interface_ {
|
||||||
|
@ -429,6 +443,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
// `json__encode` => `json__encode_User`
|
// `json__encode` => `json__encode_User`
|
||||||
name += '_' + json_type_str.replace('.', '__')
|
name += '_' + json_type_str.replace('.', '__')
|
||||||
}
|
}
|
||||||
|
if node.generic_type != table.void_type {
|
||||||
|
// `foo<int>()` => `foo_int()`
|
||||||
|
name += '_' + g.typ(node.generic_type)
|
||||||
|
}
|
||||||
// Generate tmp vars for values that have to be freed.
|
// Generate tmp vars for values that have to be freed.
|
||||||
/*
|
/*
|
||||||
mut tmps := []string{}
|
mut tmps := []string{}
|
||||||
|
|
|
@ -25,11 +25,13 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||||
p.expr_mod = ''
|
p.expr_mod = ''
|
||||||
is_or_block_used = true
|
is_or_block_used = true
|
||||||
}
|
}
|
||||||
|
mut generic_type := table.void_type
|
||||||
if p.tok.kind == .lt {
|
if p.tok.kind == .lt {
|
||||||
// `foo<int>(10)`
|
// `foo<int>(10)`
|
||||||
p.next() // `<`
|
p.next() // `<`
|
||||||
p.parse_type()
|
generic_type = p.parse_type()
|
||||||
p.check(.gt) // `>`
|
p.check(.gt) // `>`
|
||||||
|
p.table.register_fn_gen_type(fn_name, generic_type)
|
||||||
}
|
}
|
||||||
p.check(.lpar)
|
p.check(.lpar)
|
||||||
args := p.call_args()
|
args := p.call_args()
|
||||||
|
@ -40,6 +42,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||||
pos: first_pos.pos
|
pos: first_pos.pos
|
||||||
len: last_pos.pos - first_pos.pos + last_pos.len
|
len: last_pos.pos - first_pos.pos + last_pos.len
|
||||||
}
|
}
|
||||||
|
// `foo() or {}``
|
||||||
mut or_stmts := []ast.Stmt{}
|
mut or_stmts := []ast.Stmt{}
|
||||||
if p.tok.kind == .key_orelse {
|
if p.tok.kind == .key_orelse {
|
||||||
p.inside_or_expr = true
|
p.inside_or_expr = true
|
||||||
|
@ -80,6 +83,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||||
stmts: or_stmts
|
stmts: or_stmts
|
||||||
is_used: is_or_block_used
|
is_used: is_or_block_used
|
||||||
}
|
}
|
||||||
|
generic_type: generic_type
|
||||||
}
|
}
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
@ -284,6 +288,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
args: args
|
args: args
|
||||||
is_deprecated: is_deprecated
|
is_deprecated: is_deprecated
|
||||||
is_pub: is_pub
|
is_pub: is_pub
|
||||||
|
is_generic: is_generic
|
||||||
is_variadic: is_variadic
|
is_variadic: is_variadic
|
||||||
receiver: ast.Field{
|
receiver: ast.Field{
|
||||||
name: rec_name
|
name: rec_name
|
||||||
|
|
|
@ -804,7 +804,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||||
// p.warn('name expr $p.tok.lit $p.peek_tok.str()')
|
// p.warn('name expr $p.tok.lit $p.peek_tok.str()')
|
||||||
// fn call or type cast
|
// fn call or type cast
|
||||||
if p.peek_tok.kind == .lpar || (p.peek_tok.kind == .lt && p.peek_tok2.kind == .name &&
|
if p.peek_tok.kind == .lpar || (p.peek_tok.kind == .lt && p.peek_tok2.kind == .name &&
|
||||||
p.peek_tok.pos == p.peek_tok2.pos - 1) { // foo() or foo<int>()
|
p.peek_tok.pos == p.peek_tok2.pos - 1) { // foo() or foo<int>() TODO remove whitespace sensitivity
|
||||||
mut name := p.tok.lit
|
mut name := p.tok.lit
|
||||||
if mod.len > 0 {
|
if mod.len > 0 {
|
||||||
name = '${mod}.$name'
|
name = '${mod}.$name'
|
||||||
|
|
|
@ -191,6 +191,7 @@ pub const (
|
||||||
array_type_idx = 20
|
array_type_idx = 20
|
||||||
map_type_idx = 21
|
map_type_idx = 21
|
||||||
any_type_idx = 22
|
any_type_idx = 22
|
||||||
|
t_type_idx = 23
|
||||||
)
|
)
|
||||||
|
|
||||||
pub const (
|
pub const (
|
||||||
|
@ -237,6 +238,7 @@ pub const (
|
||||||
array_type = new_type(array_type_idx)
|
array_type = new_type(array_type_idx)
|
||||||
map_type = new_type(map_type_idx)
|
map_type = new_type(map_type_idx)
|
||||||
any_type = new_type(any_type_idx)
|
any_type = new_type(any_type_idx)
|
||||||
|
t_type = new_type(t_type_idx)
|
||||||
)
|
)
|
||||||
|
|
||||||
pub const (
|
pub const (
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub mut:
|
||||||
modules []string // List of all modules registered by the application
|
modules []string // List of all modules registered by the application
|
||||||
cflags []cflag.CFlag
|
cflags []cflag.CFlag
|
||||||
redefined_fns []string
|
redefined_fns []string
|
||||||
fn_gen_types map[string][]string
|
fn_gen_types map[string][]Type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Fn {
|
pub struct Fn {
|
||||||
|
@ -569,3 +569,9 @@ pub fn (table &Table) qualify_module(mod, file_path string) string {
|
||||||
}
|
}
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (table &Table) register_fn_gen_type(fn_name string, typ Type) {
|
||||||
|
mut a := table.fn_gen_types[fn_name]
|
||||||
|
a << typ
|
||||||
|
table.fn_gen_types[fn_name] = a
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
fn test_todo() {}
|
fn test_todo() {
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// QTODO
|
|
||||||
fn simple<T>(p T) T {
|
fn simple<T>(p T) T {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_generic_fn() {
|
||||||
|
assert simple<int>(1) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn sum<T>(l []T) T {
|
fn sum<T>(l []T) T {
|
||||||
mut r := T(0)
|
mut r := T(0)
|
||||||
for e in l {
|
for e in l {
|
||||||
|
|
Loading…
Reference in New Issue