v/vlib/compiler/table.v

1186 lines
26 KiB
V
Raw Normal View History

2020-01-23 21:04:46 +01:00
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
2019-06-23 04:21:30 +02:00
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module compiler
2019-06-22 20:20:28 +02:00
2019-08-17 21:19:37 +02:00
import strings
2019-06-22 20:20:28 +02:00
struct Table {
pub mut:
2019-12-21 22:54:37 +01:00
typesmap map[string]Type
consts []Var
fns map[string]Fn
obf_ids map[string]int // obf_ids['myfunction'] == 23
modules []string // List of all modules registered by the application
imports []string // List of all imports
cflags []CFlag // ['-framework Cocoa', '-lglfw3']
fn_cnt int // atomic
obfuscate bool
varg_access []VargAccess
2019-12-19 20:52:27 +01:00
// enum_vals map[string][]string
// names []Name
2019-12-21 22:54:37 +01:00
max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12
2019-12-21 01:53:58 +01:00
generic_struct_params map[string][]string
tuple_variants map[string][]string // enum( Bool(BoolExpr) )
sum_types map[string][]string // SumType -> [Variants]
2019-06-22 20:20:28 +02:00
}
struct VargAccess {
fn_name string
tok_idx int
index int
}
2019-09-26 04:28:43 +02:00
enum NameCategory {
constant
mod
var
typ
}
struct Name {
cat NameCategory
2019-10-11 05:36:46 +02:00
idx int // e.g. typ := types[name.idx]
2019-07-12 07:37:54 +02:00
}
2019-06-22 20:20:28 +02:00
enum AccessMod {
2019-12-19 20:52:27 +01:00
private // private immutable
private_mut // private mutable
public // public immutable (readonly)
public_mut // public, but mutable only in this module
global // public and mutable both inside and outside (not recommended to use, that's why it's so verbose)
}
fn (a []AccessMod) contains(b AccessMod) bool {
for elm in a {
if elm == b {
return true
}
}
return false
2019-06-22 20:20:28 +02:00
}
enum TypeCategory {
2019-08-28 16:35:44 +02:00
builtin
struct_
2019-08-31 15:38:13 +02:00
func // 2
interface_
enum_
2019-08-31 15:38:13 +02:00
union_ // 5
c_struct
c_typedef
objc_interface // 8 Objective C @interface
array
2019-10-12 03:09:37 +02:00
alias // `type myint int`
}
2019-08-29 00:52:32 +02:00
struct Var {
pub mut:
2019-12-19 20:52:27 +01:00
typ string
name string
idx int // index in the local_vars array
is_arg bool
is_const bool
args []Var // function args
attr string // [json] etc
is_mut bool
is_alloc bool
is_returned bool
ptr bool
ref bool
parent_fn string // Variables can only be defined in functions
mod string // module where this var is stored
access_mod AccessMod
is_global bool // __global (translated from C only)
is_used bool
is_changed bool
scope_level int
is_c bool // todo remove once `typ` is `Type`, not string
is_moved bool
line_nr int
token_idx int // this is a token index, which will be used by error reporting
is_for_var bool
is_public bool // for consts
2019-08-29 00:52:32 +02:00
}
2019-06-22 20:20:28 +02:00
struct Type {
pub mut:
mod string
2019-06-22 20:20:28 +02:00
name string
cat TypeCategory
2019-10-24 11:19:05 +02:00
is_public bool
2019-06-22 20:20:28 +02:00
fields []Var
methods []Fn
parent string
2019-07-10 09:48:10 +02:00
func Fn // For cat == FN (type myfn fn())
is_c bool // `C.FILE`
2019-12-19 20:52:27 +01:00
enum_vals []string
gen_types []string
default_vals []string // `struct Foo { bar int = 2 }`
2019-12-21 01:53:58 +01:00
parser_idx int
decl_tok_idx int
2019-12-05 12:09:33 +01:00
// `is_placeholder` is used for types that are not defined yet but are known to exist.
2019-06-22 20:20:28 +02:00
// It allows having things like `fn (f Foo) bar()` before `Foo` is defined.
// This information is needed in the first pass.
is_placeholder bool
2019-12-19 20:52:27 +01:00
gen_str bool // needs `.str()` method generation
is_flag bool // enum bitfield flag
// max_field_len int
2019-12-21 01:53:58 +01:00
is_generic bool
2020-02-24 21:45:47 +01:00
ctype_names []string
2019-06-22 20:20:28 +02:00
}
2019-08-29 00:52:32 +02:00
struct TypeNode {
2019-12-19 20:52:27 +01:00
mut:
2019-08-29 00:52:32 +02:00
next &TypeNode
2019-12-19 20:52:27 +01:00
typ Type
2019-08-29 00:52:32 +02:00
}
/*
2019-06-22 20:20:28 +02:00
// For debugging types
pub fn (t Type) str() string {
2019-06-22 20:20:28 +02:00
mut s := 'type "$t.name" {'
if t.fields.len > 0 {
// s += '\n $t.fields.len fields:\n'
for field in t.fields {
s += '\n $field.name $field.typ'
}
s += '\n'
}
if t.methods.len > 0 {
// s += '\n $t.methods.len methods:\n'
for method in t.methods {
s += '\n ${method.str()}'
}
s += '\n'
}
s += '}\n'
return s
}
*/
2019-06-22 20:20:28 +02:00
2019-12-19 20:52:27 +01:00
const (
c_reserved = ['delete', 'exit', 'unix',
// 'print',
// 'ok',
'error', 'malloc',
//'calloc',
'free', 'panic',
2019-12-19 20:52:27 +01:00
// Full list of C reserved words, from: https://en.cppreference.com/w/c/keyword
'auto', 'char', 'default', 'do', 'double', 'extern', 'float', 'inline', 'int', 'long', 'register', 'restrict', 'short', 'signed', 'sizeof', 'static', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while', ]
2019-06-22 20:20:28 +02:00
)
2019-09-18 14:06:34 +02:00
// This is used for debugging only
pub fn (f Fn) str() string {
2019-12-19 20:52:27 +01:00
t := Table{
}
2019-06-22 20:20:28 +02:00
str_args := f.str_args(t)
2019-12-08 20:22:47 +01:00
return '${f.name}($str_args) $f.typ'
2019-06-22 20:20:28 +02:00
}
pub fn (t &Table) debug_fns() string {
2019-08-17 21:19:37 +02:00
mut s := strings.new_builder(1000)
2019-07-29 18:21:36 +02:00
for _, f in t.fns {
2019-08-17 21:19:37 +02:00
s.writeln(f.name)
}
return s.str()
}
2019-07-29 18:21:36 +02:00
2019-06-22 20:20:28 +02:00
// fn (types array_Type) print_to_file(f string) {
// }
const (
integer_types = ['int', 'i8', 'char', 'byte', 'i16', 'u16', 'u32', 'i64', 'u64']
2019-12-19 20:52:27 +01:00
float_types = ['f32', 'f64']
2019-10-20 09:19:37 +02:00
reserved_type_param_names = ['R', 'S', 'T', 'U', 'W']
pointer_types = ['byte*', 'byteptr', 'char*', 'charptr', 'void*', 'voidptr', 'voidptr*', 'intptr']
builtin_types = ['int', 'i8', 'char', 'byte', 'i16', 'u16', 'u32', 'i64', 'u64',
'f64', 'f32', 'byteptr', 'charptr', 'voidptr', 'intptr', 'string', 'ustring']
2019-06-22 20:20:28 +02:00
)
fn is_number_type(typ string) bool {
return typ in integer_types || typ in float_types
}
fn is_integer_type(typ string) bool {
return typ in integer_types
2019-06-22 20:20:28 +02:00
}
fn is_float_type(typ string) bool {
2019-08-17 21:19:37 +02:00
return typ in float_types
2019-06-22 20:20:28 +02:00
}
2019-07-24 15:24:32 +02:00
fn is_primitive_type(typ string) bool {
2019-08-17 21:19:37 +02:00
return is_number_type(typ) || typ == 'string'
}
2019-07-24 15:24:32 +02:00
fn is_pointer_type(typ string) bool {
return typ in pointer_types
}
/*
fn (t mut Table) register_enum_val(typ, val string) {
if t.enum_vals.len == 0 {
t.enum_vals = [val]
}
}
*/
2019-12-19 20:52:27 +01:00
2019-09-01 21:51:16 +02:00
fn new_table(obfuscate bool) &Table {
2019-12-19 20:52:27 +01:00
mut t := &Table{
2019-06-22 20:20:28 +02:00
obfuscate: obfuscate
2019-12-19 20:52:27 +01:00
// enum_vals: map[string][]string
2019-12-22 22:31:28 +01:00
2019-06-22 20:20:28 +02:00
}
2019-10-24 11:47:21 +02:00
t.register_builtin('int')
t.register_builtin('size_t')
2019-06-22 20:20:28 +02:00
t.register_type_with_parent('i8', 'int')
2019-09-01 21:37:22 +02:00
t.register_type_with_parent('byte', 'int')
2019-10-01 17:08:10 +02:00
t.register_type_with_parent('char', 'int') // for C functions only, to avoid warnings
2019-06-22 20:20:28 +02:00
t.register_type_with_parent('i16', 'int')
t.register_type_with_parent('u16', 'u32')
2019-06-22 20:20:28 +02:00
t.register_type_with_parent('u32', 'int')
t.register_type_with_parent('i64', 'int')
t.register_type_with_parent('u64', 'u32')
2019-10-24 11:47:21 +02:00
t.register_builtin('byteptr')
t.register_builtin('charptr')
2019-10-24 11:47:21 +02:00
t.register_builtin('intptr')
t.register_builtin('f32')
t.register_builtin('f64')
t.register_builtin('rune')
t.register_builtin('bool')
t.register_builtin('void')
t.register_builtin('voidptr')
t.register_builtin('va_list')
2019-10-20 09:19:37 +02:00
for c in reserved_type_param_names {
2019-10-24 11:47:21 +02:00
t.register_builtin(c)
2019-10-20 09:19:37 +02:00
}
2019-10-24 11:19:05 +02:00
t.register_const('stdin', 'int', 'main', true)
t.register_const('stdout', 'int', 'main', true)
t.register_const('stderr', 'int', 'main', true)
t.register_const('errno', 'int', 'main', true)
2019-06-22 20:20:28 +02:00
t.register_type_with_parent('map_string', 'map')
t.register_type_with_parent('map_int', 'map')
return t
}
// If `name` is a reserved C keyword, returns `v_name` instead.
fn (t &Table) var_cgen_name(name string) string {
2019-11-30 13:40:08 +01:00
if name in c_reserved {
2019-06-22 20:20:28 +02:00
return 'v_$name'
}
else {
return name
}
}
fn (t mut Table) register_module(mod string) {
if mod in t.modules {
2019-06-22 20:20:28 +02:00
return
}
t.modules << mod
2019-06-22 20:20:28 +02:00
}
2019-07-29 18:21:36 +02:00
fn (p mut Parser) register_array(typ string) {
if typ.contains('*') {
println('bad arr $typ')
return
}
if !p.table.known_type(typ) {
p.register_type_with_parent(typ, 'array')
p.cgen.typedefs << 'typedef array $typ;'
}
}
fn (p mut Parser) register_map(typ string) {
if typ.contains('*') {
println('bad map $typ')
return
}
if !p.table.known_type(typ) {
p.register_type_with_parent(typ, 'map')
p.cgen.typedefs << 'typedef map $typ;'
}
}
fn (table &Table) known_mod(mod string) bool {
return mod in table.modules
2019-06-22 20:20:28 +02:00
}
2019-10-24 11:19:05 +02:00
fn (t mut Table) register_const(name, typ, mod string, is_pub bool) {
t.consts << Var{
2019-06-22 20:20:28 +02:00
name: name
typ: typ
is_const: true
2019-08-17 21:19:37 +02:00
mod: mod
2019-09-23 21:47:09 +02:00
idx: -1
2019-10-24 11:19:05 +02:00
is_public: is_pub
2019-06-22 20:20:28 +02:00
}
}
// Only for translated code
fn (p mut Parser) register_global(name, typ string) {
2019-12-19 20:52:27 +01:00
p.table.consts << Var{
2019-06-22 20:20:28 +02:00
name: name
typ: typ
is_const: true
is_global: true
2019-08-17 21:19:37 +02:00
mod: p.mod
is_mut: true
2019-09-23 21:47:09 +02:00
idx: -1
2019-06-22 20:20:28 +02:00
}
}
// Only for module functions, not methods.
// That's why searching by fn name works.
fn (t mut Table) register_fn(new_fn Fn) {
2019-08-17 21:19:37 +02:00
t.fns[new_fn.name] = new_fn
2019-06-22 20:20:28 +02:00
}
fn (table &Table) known_type(typ_ string) bool {
2019-08-17 21:19:37 +02:00
mut typ := typ_
2019-06-22 20:20:28 +02:00
// 'byte*' => look up 'byte', but don't mess up fns
if typ.ends_with('*') && !typ.contains(' ') {
2019-11-11 16:43:22 +01:00
typ = typ.replace('*', '')
2019-06-22 20:20:28 +02:00
}
t := table.typesmap[typ]
return t.name.len > 0 && !t.is_placeholder
2019-06-22 20:20:28 +02:00
}
2019-08-25 23:08:06 +02:00
fn (table &Table) known_type_fast(t &Type) bool {
return t.name != '' && !t.is_placeholder
2019-08-25 23:08:06 +02:00
}
2019-09-18 14:06:34 +02:00
fn (t &Table) find_fn(name string) ?Fn {
2019-08-17 21:19:37 +02:00
f := t.fns[name]
2019-12-19 20:52:27 +01:00
if f.name.str != 0 {
// TODO
2019-08-17 21:19:37 +02:00
return f
}
2019-09-18 14:06:34 +02:00
return none
2019-06-22 20:20:28 +02:00
}
2019-10-15 17:08:46 +02:00
fn (t &Table) find_fn_is_script(name string, is_script bool) ?Fn {
mut f := t.fns[name]
2019-12-19 20:52:27 +01:00
if f.name.str != 0 {
// TODO
2019-10-15 17:08:46 +02:00
return f
}
// V script? Try os module.
if is_script {
println('trying replace $name')
f = t.fns[name.replace('main__', 'os__')]
if f.name.str != 0 {
return f
}
}
return none
}
2019-06-22 20:20:28 +02:00
fn (t &Table) known_fn(name string) bool {
2019-12-19 20:52:27 +01:00
_ = t.find_fn(name) or {
return false
}
2019-09-18 14:06:34 +02:00
return true
2019-06-22 20:20:28 +02:00
}
fn (p &Parser) known_fn_in_mod(name string) bool {
existing_fn := p.table.find_fn(name) or {
return false
}
if existing_fn.mod == p.mod || existing_fn.mod == 'builtin' {
return true
}
return false
}
2019-06-22 20:20:28 +02:00
fn (t &Table) known_const(name string) bool {
2019-12-19 20:52:27 +01:00
_ = t.find_const(name) or {
return false
}
2019-09-19 13:19:44 +02:00
return true
2019-06-22 20:20:28 +02:00
}
2019-10-24 11:47:21 +02:00
fn (t mut Table) register_builtin(typ string) {
2019-06-22 20:20:28 +02:00
if typ.len == 0 {
return
}
if typ in t.typesmap {
return
2019-08-25 23:08:06 +02:00
}
2019-12-19 20:52:27 +01:00
t.typesmap[typ] = Type{
name: typ
is_public: true
}
2019-06-22 20:20:28 +02:00
}
fn (p mut Parser) register_type_with_parent(strtyp, parent string) {
2019-12-19 20:52:27 +01:00
typ := Type{
2019-06-22 20:20:28 +02:00
name: strtyp
parent: parent
mod: p.mod
2019-10-24 11:47:21 +02:00
is_public: true
2019-06-22 20:20:28 +02:00
}
2019-12-05 12:09:33 +01:00
p.table.register_type(typ)
2019-06-22 20:20:28 +02:00
}
fn (t mut Table) register_type_with_parent(typ, parent string) {
if typ.len == 0 {
return
}
2019-12-19 20:52:27 +01:00
t.typesmap[typ] = Type{
2019-06-22 20:20:28 +02:00
name: typ
parent: parent
2019-10-24 11:47:21 +02:00
is_public: true
2019-12-19 20:52:27 +01:00
// mod: mod
2019-12-22 22:31:28 +01:00
2019-06-22 20:20:28 +02:00
}
}
2019-12-05 12:09:33 +01:00
fn (t mut Table) register_type(typ Type) {
2019-06-22 20:20:28 +02:00
if typ.name.len == 0 {
return
}
t.typesmap[typ.name] = typ
2019-06-22 20:20:28 +02:00
}
2019-08-31 15:38:13 +02:00
fn (t mut Table) rewrite_type(typ Type) {
if typ.name.len == 0 {
return
}
2019-12-19 20:52:27 +01:00
t.typesmap[typ.name] = typ
2019-08-31 15:38:13 +02:00
}
fn (table mut Table) add_field(type_name, field_name, field_type string, is_mut bool, attr string, access_mod AccessMod) {
if type_name == '' {
print_backtrace()
verror('add_field: empty type')
2019-08-31 15:38:13 +02:00
}
mut t := table.typesmap[type_name]
2019-12-19 20:52:27 +01:00
t.fields << Var{
name: field_name
typ: field_type
2019-06-22 20:20:28 +02:00
is_mut: is_mut
attr: attr
2019-12-19 20:52:27 +01:00
parent_fn: type_name // Name of the parent type
2019-12-22 22:31:28 +01:00
2019-06-22 20:20:28 +02:00
access_mod: access_mod
}
table.typesmap[type_name] = t
2019-06-22 20:20:28 +02:00
}
2019-11-24 11:53:59 +01:00
fn (table mut Table) add_default_val(idx int, type_name, val_expr string) {
mut t := table.typesmap[type_name]
2019-11-24 13:56:14 +01:00
if t.default_vals.len == 0 {
t.default_vals = [''].repeat(t.fields.len)
2019-11-27 17:55:32 +01:00
}
2019-11-24 11:53:59 +01:00
t.default_vals[idx] = val_expr
table.typesmap[type_name] = t
}
2019-06-22 20:20:28 +02:00
fn (t &Type) has_field(name string) bool {
2019-12-19 20:52:27 +01:00
_ = t.find_field(name) or {
return false
}
2019-09-18 13:28:11 +02:00
return true
2019-06-22 20:20:28 +02:00
}
fn (t &Type) has_enum_val(name string) bool {
2019-08-17 21:19:37 +02:00
return name in t.enum_vals
}
2019-09-18 13:28:11 +02:00
fn (t &Type) find_field(name string) ?Var {
2019-06-22 20:20:28 +02:00
for field in t.fields {
if field.name == name {
return field
}
}
2019-09-18 13:28:11 +02:00
return none
2019-06-22 20:20:28 +02:00
}
fn (table &Table) type_has_field(typ &Type, name string) bool {
2019-12-19 20:52:27 +01:00
_ = table.find_field(typ, name) or {
return false
}
2019-09-18 13:28:11 +02:00
return true
2019-06-22 20:20:28 +02:00
}
2019-09-18 13:28:11 +02:00
fn (table &Table) find_field(typ &Type, name string) ?Var {
for field in typ.fields {
if field.name == name {
return field
}
}
if typ.parent != '' {
2019-06-22 20:20:28 +02:00
parent := table.find_type(typ.parent)
2019-09-18 13:28:11 +02:00
for field in parent.fields {
if field.name == name {
return field
}
}
2019-06-22 20:20:28 +02:00
}
2019-09-18 13:28:11 +02:00
return none
2019-06-22 20:20:28 +02:00
}
2019-09-18 14:37:34 +02:00
fn (p mut Parser) add_method(type_name string, f Fn) {
if !p.first_pass() && f.name != 'str' {
return
}
2019-08-31 15:38:13 +02:00
if type_name == '' {
print_backtrace()
verror('add_method: empty type')
2019-08-31 15:38:13 +02:00
}
// TODO table.typesmap[type_name].methods << f
2019-09-18 14:37:34 +02:00
mut t := p.table.typesmap[type_name]
if f.name != 'str' && f in t.methods {
p.error('redefinition of method `${type_name}.$f.name`')
}
t.methods << f
2019-09-18 14:37:34 +02:00
p.table.typesmap[type_name] = t
2019-06-22 20:20:28 +02:00
}
fn (t &Type) has_method(name string) bool {
2019-12-19 20:52:27 +01:00
_ = t.find_method(name) or {
return false
}
2019-09-18 14:37:34 +02:00
return true
2019-06-22 20:20:28 +02:00
}
fn (table &Table) type_has_method(typ &Type, name string) bool {
2019-12-19 20:52:27 +01:00
_ = table.find_method(typ, name) or {
return false
}
2019-09-18 14:37:34 +02:00
return true
2019-06-22 20:20:28 +02:00
}
2019-09-18 14:37:34 +02:00
fn (table &Table) find_method(typ &Type, name string) ?Fn {
2019-09-03 13:57:04 +02:00
t := table.typesmap[typ.name]
for method in t.methods {
if method.name == name {
return method
}
}
if typ.parent != '' {
2019-06-22 20:20:28 +02:00
parent := table.find_type(typ.parent)
2019-09-18 14:37:34 +02:00
for method in parent.methods {
if method.name == name {
return method
}
}
return none
2019-06-22 20:20:28 +02:00
}
2019-09-18 14:37:34 +02:00
return none
2019-06-22 20:20:28 +02:00
}
2019-09-18 14:37:34 +02:00
fn (t &Type) find_method(name string) ?Fn {
2019-06-22 20:20:28 +02:00
// println('$t.name find_method($name) methods.len=$t.methods.len')
for method in t.methods {
// println('method=$method.name')
if method.name == name {
return method
}
}
2019-09-18 14:37:34 +02:00
return none
2019-06-22 20:20:28 +02:00
}
2019-11-08 04:03:06 +01:00
fn (table mut Table) add_gen_type(type_name, gen_type string) {
mut t := table.typesmap[type_name]
if gen_type in t.gen_types {
2019-06-22 20:20:28 +02:00
return
}
2019-11-08 04:03:06 +01:00
t.gen_types << gen_type
table.typesmap[type_name] = t
2019-06-22 20:20:28 +02:00
}
2019-08-31 15:38:13 +02:00
fn (p &Parser) find_type(name string) Type {
2019-06-22 20:20:28 +02:00
typ := p.table.find_type(name)
2019-08-31 15:38:13 +02:00
if typ.name == '' {
return p.table.find_type(p.prepend_mod(name))
2019-06-22 20:20:28 +02:00
}
return typ
}
2019-08-31 15:38:13 +02:00
fn (t &Table) find_type(name_ string) Type {
2019-08-17 21:19:37 +02:00
mut name := name_
2019-06-22 20:20:28 +02:00
if name.ends_with('*') && !name.contains(' ') {
2019-11-11 16:43:22 +01:00
name = name.replace('*', '')
2019-06-22 20:20:28 +02:00
}
if !(name in t.typesmap) {
2019-12-19 20:52:27 +01:00
// println('ret Type')
return Type{
}
2019-06-22 20:20:28 +02:00
}
return t.typesmap[name]
2019-06-22 20:20:28 +02:00
}
fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
2019-12-22 02:34:37 +01:00
//if p.fileis('type_test') {
//println('got=$got_ exp=$expected_')
//}
2019-08-17 21:19:37 +02:00
mut got := got_
mut expected := expected_
2019-12-19 20:52:27 +01:00
// p.log('check types got="$got" exp="$expected" ')
if p.pref.translated {
2019-06-22 20:20:28 +02:00
return true
}
2019-11-08 04:03:06 +01:00
if got == expected {
2019-11-07 23:48:22 +01:00
return true
}
2019-10-20 09:19:37 +02:00
// generic return type
if expected == '_ANYTYPE_' {
p.cur_fn.typ = got
return true
}
2019-12-14 20:01:20 +01:00
if throw && p.base_type(got) == p.base_type(expected) {
return true
}
// variadic
if expected.starts_with('varg_') {
expected = expected[5..]
}
if got.starts_with('varg_') {
got = got[5..]
}
2020-01-14 13:15:04 +01:00
// fn == 0 temporary
if got == 'int' && expected.ends_with('Fn') {
return true
}
2019-06-22 20:20:28 +02:00
// Allow ints to be used as floats
2019-06-25 21:36:44 +02:00
if got == 'int' && expected == 'f32' {
2019-06-22 20:20:28 +02:00
return true
}
2019-06-25 21:36:44 +02:00
if got == 'int' && expected == 'f64' {
2019-06-22 20:20:28 +02:00
return true
}
2019-06-25 21:36:44 +02:00
if got == 'f64' && expected == 'f32' {
2019-06-22 20:20:28 +02:00
return true
}
2019-06-25 21:36:44 +02:00
if got == 'f32' && expected == 'f64' {
2019-06-22 20:20:28 +02:00
return true
}
// Allow ints to be used as longs
2019-12-19 20:52:27 +01:00
if got == 'int' && expected == 'i64' {
2019-06-22 20:20:28 +02:00
return true
}
if got == 'void*' && expected.starts_with('fn ') {
return true
}
if got.starts_with('[') && expected == 'byte*' {
return true
}
// Todo void* allows everything right now
2019-12-19 20:52:27 +01:00
if got == 'void*' || expected == 'void*' {
// || got == 'cvoid' || expected == 'cvoid' {
2019-06-22 20:20:28 +02:00
return true
}
// TODO only allow numeric consts to be assigned to bytes, and
// throw an error if they are bigger than 255
2019-12-19 20:52:27 +01:00
if got == 'int' && expected == 'byte' {
2019-06-22 20:20:28 +02:00
return true
}
2019-12-19 20:52:27 +01:00
if got == 'byteptr' && expected == 'byte*' {
2019-06-25 23:43:04 +02:00
return true
}
2019-12-19 20:52:27 +01:00
if got == 'byte*' && expected == 'byteptr' {
return true
2019-08-17 21:19:37 +02:00
}
2019-12-19 20:52:27 +01:00
if got == 'charptr' && expected == 'char*' {
2019-12-01 08:11:05 +01:00
return true
}
2019-12-19 20:52:27 +01:00
if got == 'char*' && expected == 'charptr' {
2019-12-01 08:11:05 +01:00
return true
}
2019-12-19 20:52:27 +01:00
if got == 'int' && expected == 'byte*' {
2019-06-22 20:20:28 +02:00
return true
}
2019-12-19 20:52:27 +01:00
// if got=='int' && expected=='voidptr*' {
// return true
// }
2019-06-22 20:20:28 +02:00
// byteptr += int
2019-12-19 20:52:27 +01:00
if got == 'int' && expected in ['byteptr', 'charptr'] {
2019-06-22 20:20:28 +02:00
return true
}
if got == 'Option' && expected.starts_with('Option_') {
return true
}
// lines := new_array
if got == 'array' && expected.starts_with('array_') {
return true
}
// Expected type "Option_os__File", got "os__File"
if expected.starts_with('Option_') && expected.ends_with(stringify_pointer(got)) {
2019-06-22 20:20:28 +02:00
return true
}
// NsColor* return 0
2019-08-27 03:40:25 +02:00
if expected.ends_with('*') && got == 'int' {
return true
}
// if got == 'T' || got.contains('<T>') {
// return true
// }
// if expected == 'T' || expected.contains('<T>') {
// return true
// }
// TODO fn hack
2019-12-19 20:52:27 +01:00
if got.starts_with('fn ') && (expected.ends_with('fn') || expected.ends_with('Fn')) {
return true
2019-10-20 09:19:37 +02:00
}
2020-01-22 21:30:35 +01:00
if got.starts_with('fn ') && expected.starts_with('fn ') && p.mod == 'gg2' {
return true
}
2019-08-27 03:40:25 +02:00
// Allow pointer arithmetic
2019-12-19 20:52:27 +01:00
if expected == 'void*' && got == 'int' {
2019-08-27 03:40:25 +02:00
return true
2019-06-22 20:20:28 +02:00
}
2019-12-19 20:52:27 +01:00
// if p.fileis('_test') && is_number_type(got) && is_number_type(expected) {
// p.warn('got=$got exp=$expected $p.is_const_literal')
// }
2019-11-08 00:04:00 +01:00
// Allow `myu64 == 1`, `myfloat == 2` etc
if is_integer_type(got) && is_number_type(expected) && p.is_const_literal {
return true
2019-10-20 09:19:37 +02:00
}
if expected == 'integer' {
if is_integer_type(got) {
return true
2019-12-19 20:52:27 +01:00
}
else {
p.error('expected type `$expected`, but got `$got`')
}
}
2019-06-22 20:20:28 +02:00
expected = expected.replace('*', '')
2019-12-19 20:52:27 +01:00
got = got.replace('*', '').replace('ptr', '')
2019-06-22 20:20:28 +02:00
if got != expected {
// Interface check
2019-12-22 02:34:37 +01:00
if expected.ends_with('er') || expected[0] == `I` {
2019-06-22 20:20:28 +02:00
if p.satisfies_interface(expected, got, throw) {
return true
}
}
2019-12-24 09:07:26 +01:00
// Sum type
if expected in p.table.sum_types {
2019-12-22 02:34:37 +01:00
//println('checking sum')
if got in p.table.sum_types[expected] {
2019-12-22 02:34:37 +01:00
//println('yep $expected')
2019-12-24 09:07:26 +01:00
return true
}
}
2019-06-22 20:20:28 +02:00
if !throw {
return false
}
else {
2019-12-05 16:47:29 +01:00
p.error('cannot convert `$got` to `$expected`')
2019-06-22 20:20:28 +02:00
}
}
return true
}
2019-12-19 20:52:27 +01:00
fn (p mut Parser) base_type(name string) string {
typ := p.find_type(name)
if typ.parent != '' {
return p.base_type(typ.parent)
2019-12-14 20:01:20 +01:00
}
2019-12-19 20:52:27 +01:00
return name
}
2019-12-14 20:01:20 +01:00
2019-06-22 20:20:28 +02:00
// throw by default
fn (p mut Parser) check_types(got, expected string) bool {
2019-12-19 20:52:27 +01:00
if p.first_pass() {
return true
}
2019-10-09 23:56:33 +02:00
return p.check_types2(got, expected, true)
2019-06-22 20:20:28 +02:00
}
fn (p mut Parser) check_types_no_throw(got, expected string) bool {
2019-10-09 23:56:33 +02:00
return p.check_types2(got, expected, false)
2019-06-22 20:20:28 +02:00
}
fn (p mut Parser) check_types_with_token_index(got, expected string, var_token_idx int) {
2019-10-09 23:56:33 +02:00
if !p.check_types2(got, expected, false) {
p.error_with_token_index('expected type `$expected`, but got `$got`', var_token_idx)
}
}
2019-06-22 20:20:28 +02:00
fn (p mut Parser) satisfies_interface(interface_name, _typ string, throw bool) bool {
int_typ := p.table.find_type(interface_name)
typ := p.table.find_type(_typ)
for method in int_typ.methods {
if !typ.has_method(method.name) {
// if throw {
2019-12-19 20:52:27 +01:00
p.error("type `$_typ` doesn\'t satisfy interface " + '`$interface_name` (method `$method.name` is not implemented)')
2019-06-22 20:20:28 +02:00
// }
return false
}
}
return true
}
fn (table &Table) is_interface(name string) bool {
if !(name in table.typesmap) {
return false
2019-06-22 20:20:28 +02:00
}
t := table.typesmap[name]
return t.cat == .interface_
2019-06-22 20:20:28 +02:00
}
// Do we have fn main()?
fn (t &Table) main_exists() bool {
2019-08-17 21:19:37 +02:00
for _, f in t.fns {
if f.name == 'main__main' {
2019-06-22 20:20:28 +02:00
return true
}
}
return false
}
fn (t &Table) all_test_function_names() []string {
2020-02-21 12:24:13 +01:00
mut fn_begin_test_name := ''
mut fn_end_test_name := ''
mut fn_test_names := []string
for _, f in t.fns {
if f.name.contains('__test_') {
2020-02-21 12:24:13 +01:00
fn_test_names << f.name
}
else if f.name.contains('__testsuite_begin') {
fn_begin_test_name = f.name
}
else if f.name.contains('__testsuite_end') {
fn_end_test_name = f.name
2019-10-20 09:19:37 +02:00
}
}
2020-02-21 16:29:41 +01:00
if fn_begin_test_name.len == 0 {
if fn_end_test_name.len > 0 {
fn_test_names << fn_end_test_name
}
return fn_test_names
2020-02-21 12:24:13 +01:00
}
2020-02-21 16:29:41 +01:00
else {
mut res := []string
res << fn_begin_test_name
res << fn_test_names
if fn_end_test_name.len > 0 {
res << fn_end_test_name
}
return res
2020-02-21 12:24:13 +01:00
}
}
2019-09-19 13:19:44 +02:00
fn (t &Table) find_const(name string) ?Var {
2019-12-19 20:52:27 +01:00
// println('find const l=$t.consts.len')
2019-06-22 20:20:28 +02:00
for c in t.consts {
if c.name == name {
return c
}
}
2019-09-19 13:19:44 +02:00
return none
2019-06-22 20:20:28 +02:00
}
// ('s', 'string') => 'string s'
// ('nums', '[20]byte') => 'byte nums[20]'
// ('myfn', 'fn(int) string') => 'string (*myfn)(int)'
fn (table &Table) cgen_name_type_pair(name, typ string) string {
// Special case for [10]int
if typ.len > 0 && typ[0] == `[` {
tmp := typ.all_after(']')
size := typ.all_before(']')
return '$tmp $name $size ]'
}
// fn()
else if typ.starts_with('fn (') {
T := table.find_type(typ)
if T.name == '' {
2019-10-20 09:19:37 +02:00
eprintln('function type `$typ` not found')
exit(1)
2019-06-22 20:20:28 +02:00
}
str_args := T.func.str_args(table)
return '$T.func.typ (*$name)( $str_args /*FFF*/ )'
}
// TODO tm hack, do this for all C struct args
else if typ == 'tm' {
return 'struct /*TM*/ tm $name'
2019-06-22 20:20:28 +02:00
}
return '$typ $name'
}
fn is_valid_int_const(val, typ string) bool {
// x := val.int()
match typ {
'char'{
x := val.int()
return 0 <= x && x <= 255
}
2019-12-19 20:52:27 +01:00
'byte'{
x := val.int()
return 0 <= x && x <= 255
2019-12-19 20:52:27 +01:00
}
'u16'{
x := val.u16()
return 0 <= x && x <= 65535
2019-12-19 20:52:27 +01:00
}
// case 'u32': return 0 <= x && x <= math.MaxU32
// case 'u64': return 0 <= x && x <= math.MaxU64
// ////////////
'i8'{
x := val.i8()
return -128 <= x && x <= 127
2019-12-19 20:52:27 +01:00
}
/*
2019-10-12 21:31:05 +02:00
case 'i16': return math.min_i16 <= x && x <= math.max_i16
case 'int': return math.min_i32 <= x && x <= math.max_i32
2019-10-14 07:34:44 +02:00
*/
2019-12-19 20:52:27 +01:00
// case 'i64':
// x64 := val.i64()
// return i64(-(1<<63)) <= x64 && x64 <= i64((1<<63)-1)
else {
return true}}
2019-08-17 21:19:37 +02:00
}
2019-07-29 18:21:36 +02:00
fn (p mut Parser) typ_to_fmt(typ string, level int) string {
t := p.table.find_type(typ)
if t.cat == .enum_ {
2019-07-29 18:21:36 +02:00
return '%d'
}
match typ {
2019-12-19 20:52:27 +01:00
'string' {
return '%.*s'
}
// case 'bool': return '%.*s'
'ustring' {
return '%.*s'
}
'byte', 'bool', 'int', 'char', 'i16', 'i8' {
2019-12-19 20:52:27 +01:00
return '%d'
}
2019-12-19 22:29:37 +01:00
'u16', 'u32' {
2019-12-19 20:52:27 +01:00
return '%u'
}
2019-12-19 22:29:37 +01:00
'f64', 'f32' {
2019-12-19 20:52:27 +01:00
return '%f'
}
'i64' {
return '%lld'
}
'u64' {
return '%llu'
}
2019-12-19 22:29:37 +01:00
'byte*', 'byteptr' {
2019-12-19 20:52:27 +01:00
return '%s'
}
// case 'array_string': return '%s'
// case 'array_int': return '%s'
'void' {
p.error('cannot interpolate this value')
}
else {
if typ.ends_with('*') {
return '%p'
}
2019-12-19 20:52:27 +01:00
}}
2019-08-17 21:19:37 +02:00
if t.parent != '' && level == 0 {
2019-12-19 20:52:27 +01:00
return p.typ_to_fmt(t.parent, level + 1)
2019-07-29 18:21:36 +02:00
}
return ''
}
2019-10-20 09:19:37 +02:00
fn type_to_safe_str(typ string) string {
2019-12-19 20:52:27 +01:00
r := typ.replace(' ', '').replace('(', '_').replace(')', '_')
2019-10-20 09:19:37 +02:00
return r
}
fn is_compile_time_const(s_ string) bool {
s := s_.trim_space()
2019-07-29 18:21:36 +02:00
if s == '' {
return false
}
2019-12-19 20:52:27 +01:00
if s.contains("\'") {
2019-07-29 18:21:36 +02:00
return true
}
for c in s {
2019-12-19 20:52:27 +01:00
if !((c >= `0` && c <= `9`) || c == `.`) {
2019-07-29 18:21:36 +02:00
return false
}
}
return true
}
2019-08-28 16:35:44 +02:00
fn (t &Type) contains_field_type(typ string) bool {
2019-08-29 00:52:32 +02:00
if !t.name[0].is_capital() {
return false
}
2019-08-28 16:35:44 +02:00
for field in t.fields {
if field.typ == typ {
return true
}
}
return false
}
// check for a function / variable / module typo in `name`
fn (p &Parser) identify_typo(name string) string {
// dont check if so short
2019-12-19 20:52:27 +01:00
if name.len < 2 {
return ''
}
name_dotted := mod_gen_name_rev(name.replace('__', '.'))
2019-09-15 19:07:12 +02:00
min_match := 0.50 // for dice coefficient between 0.0 - 1.0
mut output := ''
// check imported modules
2019-10-25 15:34:12 +02:00
mut n := p.table.find_misspelled_imported_mod(name_dotted, p, min_match)
if n.len > 0 {
output += '\n * module: `$n`'
}
// check consts
2019-10-25 15:34:12 +02:00
n = p.table.find_misspelled_const(name, p, min_match)
if n != '' {
output += '\n * const: `$n`'
}
// check types
2019-12-19 20:52:27 +01:00
typ,type_cat := p.table.find_misspelled_type(name, p, min_match)
if typ.len > 0 {
output += '\n * $type_cat: `$typ`'
}
// check functions
2019-10-25 15:34:12 +02:00
n = p.table.find_misspelled_fn(name, p, min_match)
if n.len > 0 {
output += '\n * function: `$n`'
}
// check function local variables
n = p.find_misspelled_local_var(name_dotted, min_match)
if n.len > 0 {
output += '\n * variable: `$n`'
}
return output
}
// compare just name part, some items are mod prefied
fn typo_compare_name_mod(a, b, b_mod string) f32 {
2019-12-19 20:52:27 +01:00
if a.len - b.len > 2 || b.len - a.len > 2 {
return 0
}
auidx := a.index('__') or {
return 0
} // TODO or {-1} once cgen lines bug is fixed //-1 }
buidx := b.index('__') or {
return 0
} // -1 }
a_mod := if auidx != -1 { mod_gen_name_rev(a[..auidx]) } else { '' }
2019-12-19 20:52:27 +01:00
a_name := if auidx != -1 { a[auidx + 2..] } else { a }
b_name := if buidx != -1 { b[buidx + 2..] } else { b }
if a_mod.len > 0 && b_mod.len > 0 && a_mod != b_mod {
return 0
}
return strings.dice_coefficient(a_name, b_name)
}
// find function with closest name to `name`
2019-10-25 15:34:12 +02:00
fn (table &Table) find_misspelled_fn(name string, p &Parser, min_match f32) string {
2019-09-13 15:15:30 +02:00
mut closest := f32(0)
mut closest_fn := ''
for _, f in table.fns {
2019-12-19 20:52:27 +01:00
if f.name.contains('__') && !p.is_mod_in_scope(f.mod) {
continue
}
c := typo_compare_name_mod(name, f.name, f.mod)
2019-10-25 15:34:12 +02:00
if c > closest {
closest = c
closest_fn = mod_gen_name_rev(f.name.replace('__', '.'))
}
}
2019-09-13 15:15:30 +02:00
return if closest >= min_match { closest_fn } else { '' }
}
// find imported module with closest name to `name`
2019-10-25 15:34:12 +02:00
fn (table &Table) find_misspelled_imported_mod(name string, p &Parser, min_match f32) string {
2019-09-13 15:15:30 +02:00
mut closest := f32(0)
mut closest_mod := ''
n1 := if name.starts_with('main.') { name[5..] } else { name }
2019-10-25 15:34:12 +02:00
for alias, mod in p.import_table.imports {
c := typo_compare_name_mod(n1, alias, '')
2019-10-25 15:34:12 +02:00
if c > closest {
closest = c
closest_mod = if alias == mod { alias } else { '$alias ($mod)' }
}
}
2019-09-13 15:15:30 +02:00
return if closest >= min_match { closest_mod } else { '' }
}
// find const with closest name to `name`
2019-10-25 15:34:12 +02:00
fn (table &Table) find_misspelled_const(name string, p &Parser, min_match f32) string {
mut closest := f32(0)
mut closest_const := ''
2019-10-25 15:34:12 +02:00
for cnst in table.consts {
2019-12-19 20:52:27 +01:00
if cnst.name.contains('__') && !p.is_mod_in_scope(cnst.mod) {
continue
}
c := typo_compare_name_mod(name, cnst.name, cnst.mod)
2019-10-25 15:34:12 +02:00
if c > closest {
closest = c
closest_const = mod_gen_name_rev(cnst.name.replace('__', '.'))
}
}
return if closest >= min_match { closest_const } else { '' }
}
// find type with closest name to `name`
2019-12-19 20:52:27 +01:00
fn (table &Table) find_misspelled_type(name string, p &Parser, min_match f32) (string,string) {
mut closest := f32(0)
mut closest_type := ''
mut type_cat := ''
for _, typ in table.typesmap {
2019-12-19 20:52:27 +01:00
if typ.name.contains('__') && !p.is_mod_in_scope(typ.mod) {
continue
}
c := typo_compare_name_mod(name, typ.name, typ.mod)
if c > closest {
closest = c
closest_type = mod_gen_name_rev(typ.name.replace('__', '.'))
type_cat = type_cat_str(typ.cat)
}
}
if closest >= min_match {
2019-12-19 20:52:27 +01:00
return closest_type,type_cat
}
2019-12-19 20:52:27 +01:00
return '',''
}
fn type_cat_str(tc TypeCategory) string {
tc_str := match tc {
2019-12-19 20:52:27 +01:00
.builtin{
'builtin'
}
.struct_{
'struct'
}
.func{
'function'
}
.interface_{
'interface'
}
.enum_{
'enum'
}
.union_{
'union'
}
.c_struct{
'C struct'
}
.c_typedef{
'C typedef'
}
.objc_interface{
'obj C interface'
}
.array{
'array'
}
.alias{
'type alias'
}
else {
'unknown'}}
return tc_str
}