checker/gen/table: impl fn types & sum/other fixes & tidy

pull/3994/head
Joe Conigliaro 2020-03-12 02:10:46 +11:00
parent 1cea85df0c
commit a1314bd199
10 changed files with 151 additions and 53 deletions

View File

@ -8,7 +8,7 @@ import (
v.table v.table
) )
pub type TypeDecl = AliasTypeDecl | SumTypeDecl pub type TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl
pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral | pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral |
FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr | FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr |
@ -128,15 +128,17 @@ pub:
pub struct Arg { pub struct Arg {
pub: pub:
typ table.Type
name string name string
is_mut bool
typ table.Type
} }
pub struct FnDecl { pub struct FnDecl {
pub: pub:
name string name string
stmts []Stmt stmts []Stmt
typ table.Type return_type table.Type
args []Arg args []Arg
is_deprecated bool is_deprecated bool
is_pub bool is_pub bool
@ -232,9 +234,9 @@ pub:
scope &Scope scope &Scope
} }
pub struct IdentFunc { pub struct IdentFn {
pub mut: pub mut:
return_type table.Type typ table.Type
} }
pub struct IdentVar { pub struct IdentVar {
@ -244,7 +246,7 @@ pub mut:
is_static bool is_static bool
} }
pub type IdentInfo = IdentFunc | IdentVar pub type IdentInfo = IdentFn | IdentVar
pub enum IdentKind { pub enum IdentKind {
unresolved unresolved
@ -459,6 +461,13 @@ pub:
sub_types []table.Type sub_types []table.Type
} }
pub struct FnTypeDecl {
pub:
name string
is_pub bool
typ table.Type
}
pub struct DeferStmt { pub struct DeferStmt {
pub: pub:
stmts []Stmt stmts []Stmt

View File

@ -48,10 +48,10 @@ pub fn (node &FnDecl) str(t &table.Table) string {
} }
} }
f.write(')') f.write(')')
if node.typ != table.void_type { if node.return_type != table.void_type {
// typ := t.type_to_str(node.typ) // typ := t.type_to_str(node.typ)
// if typ.starts_with(' // if typ.starts_with('
f.write(' ' + t.type_to_str(node.typ)) f.write(' ' + t.type_to_str(node.return_type))
} }
return f.str() return f.str()
} }

View File

@ -204,6 +204,19 @@ pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type {
f = f1 f = f1
} }
} }
// check for arg (var) of fn type
if !found {
scope := c.file.scope.innermost(call_expr.pos.pos)
if var := scope.find_var(fn_name) {
if var.typ != 0 {
vts := c.table.get_type_symbol(var.typ)
if vts.kind == .function {
f = vts.info as table.Fn
found = true
}
}
}
}
if !found { if !found {
c.error('unknown fn: $fn_name', call_expr.pos) c.error('unknown fn: $fn_name', call_expr.pos)
} }
@ -277,11 +290,17 @@ pub fn (c mut Checker) method_call_expr(method_call_expr mut ast.MethodCallExpr)
return info.elem_type return info.elem_type
} }
if method := c.table.type_find_method(typ_sym, name) { if method := c.table.type_find_method(typ_sym, name) {
if method_call_expr.args.len < method.args.len-1 {
c.error('too few arguments in call to `${typ_sym.name}.$name`', method_call_expr.pos)
}
else if !method.is_variadic && method_call_expr.args.len > method.args.len+1 {
c.error('too many arguments in call to `${typ_sym.name}.$name` ($method_call_expr.args.len instead of $method.args.len)', method_call_expr.pos)
}
// if name == 'clone' { // if name == 'clone' {
// println('CLONE nr args=$method.args.len') // println('CLONE nr args=$method.args.len')
// } // }
for i, arg_expr in method_call_expr.args { for i, arg_expr in method_call_expr.args {
c.expected_type = method.args[i].typ c.expected_type = method.args[i+1].typ
c.expr(arg_expr) c.expr(arg_expr)
} }
method_call_expr.receiver_type = method.args[0].typ method_call_expr.receiver_type = method.args[0].typ
@ -492,7 +511,7 @@ fn (c mut Checker) stmt(node ast.Stmt) {
c.expr(it.expr) c.expr(it.expr)
} }
ast.FnDecl { ast.FnDecl {
c.fn_return_type = it.typ c.fn_return_type = it.return_type
for stmt in it.stmts { for stmt in it.stmts {
c.stmt(stmt) c.stmt(stmt)
} }
@ -697,8 +716,8 @@ pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type {
} }
// second use, already resovled in unresovled branch // second use, already resovled in unresovled branch
else if ident.kind == .function { else if ident.kind == .function {
info := ident.info as ast.IdentFunc info := ident.info as ast.IdentFn
return info.return_type return info.typ
} }
// Handle indents with unresolved types during the parsing step // Handle indents with unresolved types during the parsing step
// (declared after first usage) // (declared after first usage)
@ -719,12 +738,13 @@ pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type {
} }
// Function object (not a call), e.g. `onclick(my_click)` // Function object (not a call), e.g. `onclick(my_click)`
if func := c.table.find_fn(name) { if func := c.table.find_fn(name) {
fn_type := c.table.find_or_register_fn_type(func)
ident.name = name ident.name = name
ident.kind = .function ident.kind = .function
ident.info = ast.IdentFunc{ ident.info = ast.IdentFn{
return_type: func.return_type typ: fn_type
} }
return func.return_type return fn_type
} }
} }
// TODO // TODO
@ -889,6 +909,7 @@ pub fn (c mut Checker) enum_val(node ast.EnumVal) table.Type {
typ := c.table.get_type_symbol(table.Type(typ_idx)) typ := c.table.get_type_symbol(table.Type(typ_idx))
// println('tname=$typ.name') // println('tname=$typ.name')
if typ.kind != .enum_ { if typ.kind != .enum_ {
println('# $typ.kind.str()')
c.error('not an enum', node.pos) c.error('not an enum', node.pos)
} }
// info := typ.info as table.Enum // info := typ.info as table.Enum

View File

@ -245,7 +245,7 @@ fn (g mut Gen) stmt(node ast.Stmt) {
g.write('return') g.write('return')
// multiple returns // multiple returns
if it.exprs.len > 1 { if it.exprs.len > 1 {
styp := g.typ(g.fn_decl.typ) styp := g.typ(g.fn_decl.return_type)
g.write(' ($styp){') g.write(' ($styp){')
for i, expr in it.exprs { for i, expr in it.exprs {
g.write('.arg$i=') g.write('.arg$i=')
@ -375,8 +375,8 @@ fn (g mut Gen) gen_fn_decl(it ast.FnDecl) {
if name.starts_with('_op_') { if name.starts_with('_op_') {
name = op_to_fn_name(name) name = op_to_fn_name(name)
} }
// type_name := g.table.type_to_str(it.typ) // type_name := g.table.type_to_str(it.return_type)
type_name := g.typ(it.typ) type_name := g.typ(it.return_type)
g.write('$type_name ${name}(') g.write('$type_name ${name}(')
g.definitions.write('$type_name ${name}(') g.definitions.write('$type_name ${name}(')
} }

View File

@ -37,7 +37,7 @@ pub fn (g mut JsGen) writeln(s string) {
fn (g mut JsGen) stmt(node ast.Stmt) { fn (g mut JsGen) stmt(node ast.Stmt) {
match node { match node {
ast.FnDecl { ast.FnDecl {
type_sym := g.table.get_type_symbol(it.typ) type_sym := g.table.get_type_symbol(it.return_type)
g.write('/** @return { $type_sym.name } **/\nfunction ${it.name}(') g.write('/** @return { $type_sym.name } **/\nfunction ${it.name}(')
for arg in it.args { for arg in it.args {
arg_type_sym := g.table.get_type_symbol(arg.typ) arg_type_sym := g.table.get_type_symbol(arg.typ)

View File

@ -81,27 +81,18 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
is_method = true is_method = true
p.next() p.next()
rec_name = p.check_name() rec_name = p.check_name()
if p.tok.kind == .key_mut { rec_mut = p.tok.kind == .key_mut
rec_mut = true // if rec_mut {
} // p.check(.key_mut)
// }
// TODO: talk to alex, should mut be parsed with the type like this?
// or should it be a property of the arg, like this ptr/mut becomes indistinguishable
rec_type = p.parse_type() rec_type = p.parse_type()
args << table.Var{
// Receiver is the first arg
typ: rec_type
name: rec_name
}
ast_args << ast.Arg{ ast_args << ast.Arg{
name: rec_name name: rec_name
is_mut: rec_mut
typ: rec_type typ: rec_type
} }
// p.table.register_var(table.Var{
// name: rec_name
// typ: rec_type
// })
p.scope.register_var(ast.Var{
name: rec_name
typ: rec_type
})
p.check(.rpar) p.check(.rpar)
} }
mut name := '' mut name := ''
@ -126,6 +117,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
for ast_arg in ast_args { for ast_arg in ast_args {
var := table.Var{ var := table.Var{
name: ast_arg.name name: ast_arg.name
is_mut: ast_arg.is_mut
typ: ast_arg.typ typ: ast_arg.typ
} }
args << var args << var
@ -136,9 +128,9 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
// p.table.register_var(var) // p.table.register_var(var)
} }
// Return type // Return type
mut typ := table.void_type mut return_type := table.void_type
if p.tok.kind.is_start_of_type() { if p.tok.kind.is_start_of_type() {
typ = p.parse_type() return_type = p.parse_type()
} }
// Register // Register
if is_method { if is_method {
@ -147,7 +139,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
type_sym.register_method(table.Fn{ type_sym.register_method(table.Fn{
name: name name: name
args: args args: args
return_type: typ return_type: return_type
}) })
} }
else { else {
@ -160,7 +152,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
p.table.register_fn(table.Fn{ p.table.register_fn(table.Fn{
name: name name: name
args: args args: args
return_type: typ return_type: return_type
is_variadic: is_variadic is_variadic: is_variadic
is_c: is_c is_c: is_c
}) })
@ -175,7 +167,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
return ast.FnDecl{ return ast.FnDecl{
name: name name: name
stmts: stmts stmts: stmts
typ: typ return_type: return_type
args: ast_args args: ast_args
is_deprecated: is_deprecated is_deprecated: is_deprecated
is_pub: is_pub is_pub: is_pub
@ -202,6 +194,10 @@ fn (p mut Parser) fn_args() ([]ast.Arg,bool) {
mut arg_no := 1 mut arg_no := 1
for p.tok.kind != .rpar { for p.tok.kind != .rpar {
arg_name := 'arg_$arg_no' arg_name := 'arg_$arg_no'
is_mut := p.tok.kind == .key_mut
if is_mut {
p.check(.key_mut)
}
if p.tok.kind == .ellipsis { if p.tok.kind == .ellipsis {
p.check(.ellipsis) p.check(.ellipsis)
is_variadic = true is_variadic = true
@ -218,6 +214,7 @@ fn (p mut Parser) fn_args() ([]ast.Arg,bool) {
} }
args << ast.Arg{ args << ast.Arg{
name: arg_name name: arg_name
is_mut: is_mut
typ: arg_type typ: arg_type
} }
} }
@ -231,7 +228,8 @@ fn (p mut Parser) fn_args() ([]ast.Arg,bool) {
p.check(.comma) p.check(.comma)
arg_names << p.check_name() arg_names << p.check_name()
} }
if p.tok.kind == .key_mut { is_mut := p.tok.kind == .key_mut
if is_mut {
p.check(.key_mut) p.check(.key_mut)
} }
if p.tok.kind == .ellipsis { if p.tok.kind == .ellipsis {
@ -245,6 +243,7 @@ fn (p mut Parser) fn_args() ([]ast.Arg,bool) {
for arg_name in arg_names { for arg_name in arg_names {
args << ast.Arg{ args << ast.Arg{
name: arg_name name: arg_name
is_mut: is_mut
typ: typ typ: typ
} }
// if typ.typ.kind == .variadic && p.tok.kind == .comma { // if typ.typ.kind == .variadic && p.tok.kind == .comma {

View File

@ -66,15 +66,32 @@ pub fn (p mut Parser) parse_multi_return_type() table.Type {
return table.new_type(idx) return table.new_type(idx)
} }
pub fn (p mut Parser) parse_fn_type() table.Type { // given anon name based off signature when `name` is blank
// p.warn('parrse fn') pub fn (p mut Parser) parse_fn_type(name string) table.Type {
// p.warn('parse fn')
p.check(.key_fn) p.check(.key_fn)
// p.fn_decl() ast_args, is_variadic := p.fn_args()
p.fn_args() mut args := []table.Var
if p.tok.kind.is_start_of_type() { for ast_arg in ast_args {
p.parse_type() arg := table.Var{
name: ast_arg.name
is_mut: ast_arg.is_mut
typ: ast_arg.typ
} }
return table.int_type args << arg
}
mut return_type := table.void_type
if p.tok.kind.is_start_of_type() {
return_type = p.parse_type()
}
func := table.Fn{
name: name
args: args
is_variadic: is_variadic
return_type: return_type
}
idx := p.table.find_or_register_fn_type(func)
return table.new_type(idx)
} }
pub fn (p mut Parser) parse_type() table.Type { pub fn (p mut Parser) parse_type() table.Type {
@ -137,7 +154,7 @@ pub fn (p mut Parser) parse_any_type(is_c, is_ptr bool) table.Type {
match p.tok.kind { match p.tok.kind {
// func // func
.key_fn { .key_fn {
return p.parse_fn_type() return p.parse_fn_type('')
} }
// array // array
.lsbr { .lsbr {

View File

@ -1727,6 +1727,16 @@ fn (p mut Parser) type_decl() ast.TypeDecl {
sub_types: sum_variants sub_types: sum_variants
} }
} }
// function type: `type mycallback fn(string, int)`
if p.tok.kind == .key_fn {
fn_name := p.prepend_mod(name)
fn_type := p.parse_fn_type(fn_name)
return ast.FnTypeDecl{
name: fn_name
is_pub: is_pub
typ: fn_type
}
}
// type MyType int // type MyType int
parent_type := p.parse_type() parent_type := p.parse_type()
pid := table.type_idx(parent_type) pid := table.type_idx(parent_type)

View File

@ -8,7 +8,7 @@ import (
) )
pub type TypeInfo = Array | ArrayFixed | Map | Struct | pub type TypeInfo = Array | ArrayFixed | Map | Struct |
MultiReturn | Alias | Enum | SumType MultiReturn | Alias | Enum | SumType | Fn
pub struct TypeSymbol { pub struct TypeSymbol {
pub: pub:
@ -87,6 +87,7 @@ pub enum Kind {
sum_type sum_type
alias alias
enum_ enum_
function
} }
pub fn (t &TypeSymbol) str() string { pub fn (t &TypeSymbol) str() string {

View File

@ -60,6 +60,25 @@ pub fn (t mut Table) register_global(name string, typ Type) {
} }
} }
// used to compare fn's & for naming anon fn's
pub fn (f &Fn) signature() string {
mut sig := ''
for i, arg in f.args {
// TODO: for now ignore mut/pts in sig for now
typ := type_set_nr_muls(arg.typ, 0)
// if arg.is_mut {
// sig += 'mut_'
// }
// sig += '$arg.typ'
sig += '$typ'
if i < f.args.len-1 {
sig += '_'
}
}
sig += '_$f.return_type'
return sig
}
pub fn (t &Table) find_fn(name string) ?Fn { pub fn (t &Table) find_fn(name string) ?Fn {
f := t.fns[name] f := t.fns[name]
if f.name.str != 0 { if f.name.str != 0 {
@ -360,6 +379,20 @@ pub fn (t mut Table) find_or_register_multi_return(mr_typs []Type) int {
return t.register_type_symbol(mr_type) return t.register_type_symbol(mr_type)
} }
pub fn (t mut Table) find_or_register_fn_type(f Fn) int {
name := if f.name.len > 0 {
f.name
}
else {
'anon_$f.signature()'
}
return t.register_type_symbol(TypeSymbol{
kind: .function
name: name
info: f
})
}
pub fn (t mut Table) add_placeholder_type(name string) int { pub fn (t mut Table) add_placeholder_type(name string) int {
ph_type := TypeSymbol{ ph_type := TypeSymbol{
kind: .placeholder kind: .placeholder
@ -440,12 +473,20 @@ pub fn (t &Table) check(got, expected Type) bool {
return true return true
} }
} }
else if exp_type_sym.kind == .sum_type { if exp_type_sym.kind == .sum_type {
sum_info := exp_type_sym.info as SumType sum_info := exp_type_sym.info as SumType
if got in sum_info.variants { if got in sum_info.variants {
return true return true
} }
} }
// fn type
if got_type_sym.kind == .function && exp_type_sym.kind == .function {
got_fn := got_type_sym.info as Fn
exp_fn := exp_type_sym.info as Fn
if got_fn.signature() == exp_fn.signature() {
return true
}
}
if got_idx != exp_idx { if got_idx != exp_idx {
// && got.typ.name != expected.typ.name*/ // && got.typ.name != expected.typ.name*/
return false return false