1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/compiler/table.v

988 lines
22 KiB
V
Raw Normal View History

2019-06-23 05:21:30 +03:00
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
2019-06-22 21:20:28 +03:00
module main
2019-08-17 22:19:37 +03:00
import math
import strings
2019-06-22 21:20:28 +03:00
struct Table {
mut:
typesmap map[string]Type
consts []Var
2019-08-17 22:19:37 +03:00
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
modules []string // List of all modules registered by the application
imports []string // List of all imports
file_imports []FileImportTable // List of imports for file
cflags []CFlag // ['-framework Cocoa', '-lglfw3']
2019-08-24 14:35:05 +03:00
fn_cnt int //atomic
obfuscate bool
2019-06-22 21:20:28 +03:00
}
2019-07-29 19:21:36 +03:00
struct GenTable {
2019-08-17 22:19:37 +03:00
fn_name string
mut:
2019-08-17 22:19:37 +03:00
types []string
}
2019-07-29 19:21:36 +03:00
2019-07-12 08:37:54 +03:00
// Holds import information scoped to the parsed file
struct FileImportTable {
mut:
2019-07-21 18:53:35 +03:00
module_name string
file_path string
imports map[string]string
2019-07-12 08:37:54 +03:00
}
2019-06-22 21:20:28 +03:00
enum AccessMod {
2019-08-17 22:19:37 +03:00
private // private immutable
private_mut // private mutable
2019-07-29 19:21:36 +03:00
public // public immutable (readonly)
public_mut // public, but mutable only in this module
public_mut_mut // public and mutable both inside and outside (not recommended to use, that's why it's so verbose)
2019-06-22 21:20:28 +03:00
}
enum TypeCategory {
2019-08-28 17:35:44 +03:00
builtin
struct_
2019-08-31 16:38:13 +03:00
func // 2
interface_
enum_
2019-08-31 16:38:13 +03:00
union_ // 5
c_struct
c_typedef
array
}
2019-08-29 01:52:32 +03:00
struct Var {
mut:
typ string
name string
is_arg bool
is_const bool
args []Var // function args
attr string // [json] etc
is_mut bool
is_alloc bool
is_returned bool
2019-08-29 01:52:32 +03:00
ptr bool
ref bool
parent_fn string // Variables can only be defined in functions
mod string // module where this var is stored
line_nr int
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
2019-08-29 01:52:32 +03:00
}
2019-06-22 21:20:28 +03:00
struct Type {
mut:
mod string
2019-06-22 21:20:28 +03:00
name string
cat TypeCategory
2019-06-22 21:20:28 +03:00
fields []Var
methods []Fn
parent string
2019-07-10 10:48:10 +03:00
func Fn // For cat == FN (type myfn fn())
is_c bool // `C.FILE`
2019-08-17 22:19:37 +03:00
enum_vals []string
gen_types []string
2019-06-22 21:20:28 +03:00
// This field is used for types that are not defined yet but are known to exist.
// 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-08-17 22:19:37 +03:00
gen_str bool // needs `.str()` method generation
2019-06-22 21:20:28 +03:00
}
2019-08-29 01:52:32 +03:00
struct TypeNode {
mut:
next &TypeNode
typ Type
}
2019-06-22 21:20:28 +03:00
// For debugging types
fn (t Type) str() string {
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
}
const (
CReserved = [
'exit',
'unix',
//'print',
2019-06-22 21:20:28 +03:00
// 'ok',
'error',
'malloc',
'calloc',
'free',
'panic',
// Full list of C reserved words, from: https://en.cppreference.com/w/c/keyword
'auto',
'break',
'case',
'char',
'const',
'continue',
'default',
'do',
'double',
'else',
'enum',
'extern',
'float',
'for',
'goto',
'if',
'inline',
'int',
'long',
'register',
'restrict',
'return',
'short',
'signed',
'sizeof',
'static',
'struct',
'switch',
'typedef',
'union',
'unsigned',
'void',
'volatile',
'while',
2019-06-22 21:20:28 +03:00
]
2019-06-22 21:20:28 +03:00
)
// This is used in generated C code
fn (f Fn) str() string {
t := Table{}
str_args := f.str_args(t)
return '$f.name($str_args) $f.typ'
}
2019-07-29 19:21:36 +03:00
fn (t &Table) debug_fns() string {
2019-08-17 22:19:37 +03:00
mut s := strings.new_builder(1000)
2019-07-29 19:21:36 +03:00
for _, f in t.fns {
2019-08-17 22:19:37 +03:00
s.writeln(f.name)
}
return s.str()
}
2019-07-29 19:21:36 +03:00
2019-06-22 21:20:28 +03:00
// fn (types array_Type) print_to_file(f string) {
// }
const (
2019-09-01 22:37:22 +03:00
number_types = ['number', 'int', 'i8', 'i16', 'u16', 'u32', 'byte', 'i64', 'u64', 'f32', 'f64']
float_types = ['f32', 'f64']
2019-06-22 21:20:28 +03:00
)
fn is_number_type(typ string) bool {
2019-08-17 22:19:37 +03:00
return typ in number_types
2019-06-22 21:20:28 +03:00
}
fn is_float_type(typ string) bool {
2019-08-17 22:19:37 +03:00
return typ in float_types
2019-06-22 21:20:28 +03:00
}
2019-07-24 16:24:32 +03:00
fn is_primitive_type(typ string) bool {
2019-08-17 22:19:37 +03:00
return is_number_type(typ) || typ == 'string'
}
2019-07-24 16:24:32 +03:00
2019-09-01 22:51:16 +03:00
fn new_table(obfuscate bool) &Table {
2019-06-22 21:20:28 +03:00
mut t := &Table {
obfuscate: obfuscate
}
t.register_type('int')
t.register_type('size_t')
t.register_type_with_parent('i8', 'int')
2019-09-01 22:37:22 +03:00
t.register_type_with_parent('byte', 'int')
2019-06-22 21:20:28 +03:00
t.register_type_with_parent('i16', 'int')
t.register_type_with_parent('u16', 'u32')
2019-06-22 21:20:28 +03:00
t.register_type_with_parent('u32', 'int')
t.register_type_with_parent('i64', 'int')
t.register_type_with_parent('u64', 'u32')
2019-06-22 21:20:28 +03:00
t.register_type('byteptr')
t.register_type('intptr')
t.register_type('f32')
t.register_type('f64')
t.register_type('rune')
t.register_type('bool')
t.register_type('void')
t.register_type('voidptr')
2019-07-29 19:21:36 +03:00
t.register_type('T')
2019-06-22 21:20:28 +03:00
t.register_type('va_list')
2019-08-22 23:19:31 +03:00
t.register_const('stdin', 'int', 'main')
t.register_const('stdout', 'int', 'main')
t.register_const('stderr', 'int', 'main')
t.register_const('errno', 'int', 'main')
2019-06-22 21:20:28 +03: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 mut Table) var_cgen_name(name string) string {
if name in CReserved {
2019-06-22 21:20:28 +03:00
return 'v_$name'
}
else {
return name
}
}
fn (t mut Table) register_module(mod string) {
if mod in t.modules {
2019-06-22 21:20:28 +03:00
return
}
t.modules << mod
2019-06-22 21:20:28 +03:00
}
2019-07-29 19:21:36 +03: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 21:20:28 +03:00
}
2019-08-22 23:19:31 +03:00
fn (t mut Table) register_const(name, typ, mod string) {
2019-06-22 21:20:28 +03:00
t.consts << Var {
name: name
typ: typ
is_const: true
2019-08-17 22:19:37 +03:00
mod: mod
2019-06-22 21:20:28 +03:00
}
}
// Only for translated code
fn (p mut Parser) register_global(name, typ string) {
p.table.consts << Var {
name: name
typ: typ
is_const: true
is_global: true
2019-08-17 22:19:37 +03:00
mod: p.mod
is_mut: true
2019-06-22 21:20:28 +03:00
}
}
fn (t mut Table) register_fn(new_fn Fn) {
2019-08-17 22:19:37 +03:00
t.fns[new_fn.name] = new_fn
2019-06-22 21:20:28 +03:00
}
fn (table &Table) known_type(typ_ string) bool {
2019-08-17 22:19:37 +03:00
mut typ := typ_
2019-06-22 21:20:28 +03:00
// 'byte*' => look up 'byte', but don't mess up fns
if typ.ends_with('*') && !typ.contains(' ') {
typ = typ.left(typ.len - 1)
}
t := table.typesmap[typ]
return t.name.len > 0 && !t.is_placeholder
2019-06-22 21:20:28 +03:00
}
2019-08-26 00:08:06 +03:00
fn (table &Table) known_type_fast(t &Type) bool {
return t.name.len > 0 && !t.is_placeholder
}
2019-06-22 21:20:28 +03:00
fn (t &Table) find_fn(name string) Fn {
2019-08-17 22:19:37 +03:00
f := t.fns[name]
if !isnil(f.name.str) {
return f
}
2019-06-22 21:20:28 +03:00
return Fn{}
}
fn (t &Table) known_fn(name string) bool {
2019-08-17 22:19:37 +03:00
f := t.find_fn(name)
return f.name != ''
2019-06-22 21:20:28 +03:00
}
fn (t &Table) known_const(name string) bool {
v := t.find_const(name)
// TODO use optional
return v.name.len > 0
}
fn (t mut Table) register_type(typ string) {
if typ.len == 0 {
return
}
if typ in t.typesmap {
return
2019-08-26 00:08:06 +03:00
}
t.typesmap[typ] = Type{name:typ}
2019-06-22 21:20:28 +03:00
}
fn (p mut Parser) register_type_with_parent(strtyp, parent string) {
typ := Type {
name: strtyp
parent: parent
mod: p.mod
2019-06-22 21:20:28 +03:00
}
p.table.register_type2(typ)
}
fn (t mut Table) register_type_with_parent(typ, parent string) {
if typ.len == 0 {
return
}
t.typesmap[typ] = Type {
2019-06-22 21:20:28 +03:00
name: typ
parent: parent
2019-08-17 22:19:37 +03:00
//mod: mod
2019-06-22 21:20:28 +03:00
}
}
fn (t mut Table) register_type2(typ Type) {
if typ.name.len == 0 {
return
}
t.typesmap[typ.name] = typ
2019-06-22 21:20:28 +03:00
}
2019-08-31 16:38:13 +03:00
fn (t mut Table) rewrite_type(typ Type) {
if typ.name.len == 0 {
return
}
t.typesmap[typ.name] = typ
2019-08-31 16:38:13 +03: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()
cerror('add_field: empty type')
}
mut t := table.typesmap[type_name]
t.fields << Var {
name: field_name
typ: field_type
2019-06-22 21:20:28 +03:00
is_mut: is_mut
attr: attr
parent_fn: type_name // Name of the parent type
2019-06-22 21:20:28 +03:00
access_mod: access_mod
}
table.typesmap[type_name] = t
2019-06-22 21:20:28 +03:00
}
fn (t &Type) has_field(name string) bool {
field := t.find_field(name)
return (field.name != '')
}
fn (t &Type) has_enum_val(name string) bool {
2019-08-17 22:19:37 +03:00
return name in t.enum_vals
}
2019-06-22 21:20:28 +03:00
fn (t &Type) find_field(name string) Var {
for field in t.fields {
if field.name == name {
return field
}
}
return Var{}
}
fn (table &Table) type_has_field(typ &Type, name string) bool {
field := table.find_field(typ, name)
return (field.name != '')
}
fn (table &Table) find_field(typ &Type, name string) Var {
field := typ.find_field(name)
if field.name.len == 0 && typ.parent.len > 0 {
parent := table.find_type(typ.parent)
return parent.find_field(name)
}
return field
}
2019-08-31 16:38:13 +03:00
fn (table mut Table) add_method(type_name string, f Fn) {
if type_name == '' {
print_backtrace()
cerror('add_method: empty type')
}
// TODO table.typesmap[type_name].methods << f
mut t := table.typesmap[type_name]
t.methods << f
table.typesmap[type_name] = t
2019-06-22 21:20:28 +03:00
}
fn (t &Type) has_method(name string) bool {
method := t.find_method(name)
return (method.name != '')
}
fn (table &Table) type_has_method(typ &Type, name string) bool {
method := table.find_method(typ, name)
return (method.name != '')
}
// TODO use `?Fn`
fn (table &Table) find_method(typ &Type, name string) Fn {
// println('TYPE HAS METHOD $name')
2019-09-03 14:57:04 +03:00
// method := typ.find_method(name)
t := table.typesmap[typ.name]
method := t.find_method(name)
2019-06-22 21:20:28 +03:00
if method.name.len == 0 && typ.parent.len > 0 {
parent := table.find_type(typ.parent)
return parent.find_method(name)
// println('parent = $parent.name $res')
// return res
}
return method
}
fn (t &Type) find_method(name string) Fn {
// 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
}
}
return Fn{}
}
2019-08-17 22:19:37 +03:00
/*
2019-08-31 16:38:13 +03:00
// TODO
fn (t mutt Type) add_gen_type(type_name string) {
2019-06-22 21:20:28 +03:00
// println('add_gen_type($s)')
if t.gen_types.contains(type_name) {
return
}
t.gen_types << type_name
}
2019-08-17 22:19:37 +03:00
*/
2019-06-22 21:20:28 +03:00
2019-08-31 16:38:13 +03:00
fn (p &Parser) find_type(name string) Type {
2019-06-22 21:20:28 +03:00
typ := p.table.find_type(name)
2019-08-31 16:38:13 +03:00
if typ.name == '' {
return p.table.find_type(p.prepend_mod(name))
2019-06-22 21:20:28 +03:00
}
return typ
}
2019-08-31 16:38:13 +03:00
fn (t &Table) find_type(name_ string) Type {
2019-08-17 22:19:37 +03:00
mut name := name_
2019-06-22 21:20:28 +03:00
if name.ends_with('*') && !name.contains(' ') {
name = name.left(name.len - 1)
}
if !(name in t.typesmap) {
return Type{}
2019-06-22 21:20:28 +03:00
}
return t.typesmap[name]
2019-06-22 21:20:28 +03:00
}
fn (p mut Parser) _check_types(got_, expected_ string, throw bool) bool {
2019-08-17 22:19:37 +03:00
mut got := got_
mut expected := expected_
2019-06-22 21:20:28 +03:00
p.log('check types got="$got" exp="$expected" ')
if p.pref.translated {
2019-06-22 21:20:28 +03:00
return true
}
// Allow ints to be used as floats
2019-06-25 22:36:44 +03:00
if got == 'int' && expected == 'f32' {
2019-06-22 21:20:28 +03:00
return true
}
2019-06-25 22:36:44 +03:00
if got == 'int' && expected == 'f64' {
2019-06-22 21:20:28 +03:00
return true
}
2019-06-25 22:36:44 +03:00
if got == 'f64' && expected == 'f32' {
2019-06-22 21:20:28 +03:00
return true
}
2019-06-25 22:36:44 +03:00
if got == 'f32' && expected == 'f64' {
2019-06-22 21:20:28 +03:00
return true
}
// Allow ints to be used as longs
2019-06-25 23:19:17 +03:00
if got=='int' && expected=='i64' {
2019-06-22 21:20:28 +03: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
if got=='void*' || expected=='void*' {// || got == 'cvoid' || expected == 'cvoid' {
2019-06-22 21:20:28 +03:00
return true
}
// TODO only allow numeric consts to be assigned to bytes, and
// throw an error if they are bigger than 255
if got=='int' && expected=='byte' {
2019-06-22 21:20:28 +03:00
return true
}
if got=='byteptr' && expected=='byte*' {
2019-06-26 00:43:04 +03:00
return true
}
if got=='byte*' && expected=='byteptr' {
return true
2019-08-17 22:19:37 +03:00
}
if got=='int' && expected=='byte*' {
2019-06-22 21:20:28 +03:00
return true
}
2019-08-03 10:44:08 +03:00
//if got=='int' && expected=='voidptr*' {
2019-08-17 22:19:37 +03:00
//return true
//}
2019-06-22 21:20:28 +03:00
// byteptr += int
if got=='int' && expected=='byteptr' {
2019-06-22 21:20:28 +03: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(got) {
return true
}
// NsColor* return 0
2019-08-27 04:40:25 +03: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
// }
// Allow pointer arithmetic
if expected=='void*' && got=='int' {
return true
2019-06-22 21:20:28 +03:00
}
expected = expected.replace('*', '')
got = got.replace('*', '')
if got != expected {
// Interface check
if expected.ends_with('er') {
if p.satisfies_interface(expected, got, throw) {
return true
}
}
if !throw {
return false
}
else {
p.error('expected type `$expected`, but got `$got`')
}
}
return true
}
// throw by default
fn (p mut Parser) check_types(got, expected string) bool {
2019-08-31 16:38:13 +03:00
if p.first_pass() { return true }
2019-06-22 21:20:28 +03:00
return p._check_types(got, expected, true)
}
fn (p mut Parser) check_types_no_throw(got, expected string) bool {
return p._check_types(got, expected, false)
}
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-06-23 03:02:33 +03:00
p.error('Type "$_typ" doesn\'t satisfy interface "$interface_name" (method "$method.name" is not implemented)')
2019-06-22 21:20:28 +03:00
// }
return false
}
}
return true
}
fn type_default(typ string) string {
if typ.starts_with('array_') {
return 'new_array(0, 1, sizeof( ${typ.right(6)} ))'
2019-06-22 21:20:28 +03:00
}
// Always set pointers to 0
if typ.ends_with('*') {
return '0'
}
2019-08-17 22:19:37 +03:00
// User struct defined in another module.
2019-06-22 21:20:28 +03:00
if typ.contains('__') {
return '{0}'
2019-06-22 21:20:28 +03:00
}
// Default values for other types are not needed because of mandatory initialization
switch typ {
case 'bool': return '0'
case 'string': return 'tos((byte *)"", 0)'
2019-07-15 13:33:18 +03:00
case 'i8': return '0'
case 'i16': return '0'
2019-07-18 05:32:49 +03:00
case 'i64': return '0'
2019-07-15 13:33:18 +03:00
case 'u16': return '0'
case 'u32': return '0'
2019-07-18 05:32:49 +03:00
case 'u64': return '0'
2019-07-15 13:33:18 +03:00
case 'byte': return '0'
case 'int': return '0'
case 'rune': return '0'
case 'f32': return '0.0'
case 'f64': return '0.0'
case 'byteptr': return '0'
case 'voidptr': return '0'
2019-06-22 21:20:28 +03:00
}
return '{0}'
2019-06-22 21:20:28 +03:00
}
fn (table &Table) is_interface(name string) bool {
if !(name in table.typesmap) {
return false
2019-06-22 21:20:28 +03:00
}
t := table.typesmap[name]
return t.cat == .interface_
2019-06-22 21:20:28 +03:00
}
// Do we have fn main()?
fn (t &Table) main_exists() bool {
2019-08-17 22:19:37 +03:00
for _, f in t.fns {
2019-06-22 21:20:28 +03:00
if f.name == 'main' {
return true
}
}
return false
}
// TODO use `?Var`
fn (t &Table) find_const(name string) Var {
2019-08-26 00:08:06 +03:00
//println('find const l=$t.consts.len')
2019-06-22 21:20:28 +03:00
for c in t.consts {
if c.name == name {
return c
}
}
return Var{}
}
fn (table mut Table) cgen_name(f &Fn) string {
mut name := f.name
if f.is_method {
name = '${f.receiver_typ}_$f.name'
name = name.replace(' ', '')
name = name.replace('*', '')
name = name.replace('+', 'plus')
name = name.replace('-', 'minus')
}
// Avoid name conflicts (with things like abs(), print() etc).
// Generate b_abs(), b_print()
// TODO duplicate functionality
if f.mod == 'builtin' && f.name in CReserved {
2019-06-22 21:20:28 +03:00
return 'v_$name'
}
// Obfuscate but skip certain names
// TODO ugly, fix
if table.obfuscate && f.name != 'main' && f.name != 'WinMain' && f.mod != 'builtin' && !f.is_c &&
f.mod != 'darwin' && f.mod != 'os' && !f.name.contains('window_proc') && f.name != 'gg__vec2' &&
f.name != 'build_token_str' && f.name != 'build_keys' && f.mod != 'json' &&
2019-06-22 21:20:28 +03:00
!name.ends_with('_str') && !name.contains('contains') {
mut idx := table.obf_ids[name]
// No such function yet, register it
if idx == 0 {
table.fn_cnt++
table.obf_ids[name] = table.fn_cnt
idx = table.fn_cnt
}
old := name
name = 'f_$idx'
2019-06-23 11:01:55 +03:00
println('$old ==> $name')
2019-06-22 21:20:28 +03:00
}
return name
}
// ('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 == '' {
println('this should never happen')
exit(1)
2019-06-22 21:20:28 +03: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 21:20:28 +03:00
}
return '$typ $name'
}
fn is_valid_int_const(val, typ string) bool {
2019-08-17 22:19:37 +03:00
x := val.int()
switch typ {
case 'byte': return 0 <= x && x <= math.MaxU8
2019-08-17 22:19:37 +03:00
case 'u16': return 0 <= x && x <= math.MaxU16
//case 'u32': return 0 <= x && x <= math.MaxU32
//case 'u64': return 0 <= x && x <= math.MaxU64
//////////////
case 'i8': return math.MinI8 <= x && x <= math.MaxI8
case 'i16': return math.MinI16 <= x && x <= math.MaxI16
2019-09-01 22:37:22 +03:00
case 'int': return math.MinI32 <= x && x <= math.MaxI32
2019-08-17 22:19:37 +03:00
//case 'i64':
//x64 := val.i64()
//return i64(-(1<<63)) <= x64 && x64 <= i64((1<<63)-1)
}
return true
}
fn (t mut Table) register_generic_fn(fn_name string) {
t.generic_fns << GenTable{fn_name, []string}
}
fn (t mut Table) fn_gen_types(fn_name string) []string {
2019-07-29 19:21:36 +03:00
for _, f in t.generic_fns {
if f.fn_name == fn_name {
return f.types
2019-08-17 22:19:37 +03:00
}
}
2019-08-29 03:30:17 +03:00
cerror('function $fn_name not found')
return []string
2019-08-17 22:19:37 +03:00
}
2019-07-29 19:21:36 +03:00
// `foo<Bar>()`
// fn_name == 'foo'
2019-08-17 22:19:37 +03:00
// typ == 'Bar'
fn (t mut Table) register_generic_fn_type(fn_name, typ string) {
2019-07-29 19:21:36 +03:00
for i, f in t.generic_fns {
if f.fn_name == fn_name {
2019-08-17 22:19:37 +03:00
t.generic_fns[i].types << typ
return
}
}
}
2019-07-29 19:21:36 +03: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 19:21:36 +03:00
return '%d'
}
switch typ {
case 'string': return '%.*s'
2019-08-09 19:10:59 +03:00
//case 'bool': return '%.*s'
2019-07-29 19:21:36 +03:00
case 'ustring': return '%.*s'
2019-09-01 22:37:22 +03:00
case 'byte', 'bool', 'int', 'char', 'byte', 'i16', 'i8': return '%d'
case 'u16', 'u32': return '%u'
2019-07-29 19:21:36 +03:00
case 'f64', 'f32': return '%f'
case 'i64': return '%lld'
case 'u64': return '%llu'
2019-07-29 19:21:36 +03:00
case 'byte*', 'byteptr': return '%s'
// case 'array_string': return '%s'
// case 'array_int': return '%s'
case 'void': p.error('cannot interpolate this value')
default:
if typ.ends_with('*') {
2019-08-17 22:19:37 +03:00
return '%p'
}
}
if t.parent != '' && level == 0 {
return p.typ_to_fmt(t.parent, level+1)
2019-07-29 19:21:36 +03:00
}
return ''
}
fn is_compile_time_const(s_ string) bool {
s := s_.trim_space()
2019-07-29 19:21:36 +03:00
if s == '' {
return false
}
if s.contains('\'') {
return true
}
for c in s {
if ! ((c >= `0` && c <= `9`) || c == `.`) {
return false
}
}
return true
}
2019-07-12 08:37:54 +03:00
// Once we have a module format we can read from module file instead
// this is not optimal
fn (table &Table) qualify_module(mod string, file_path string) string {
for m in table.imports {
if m.contains('.') && m.contains(mod) {
m_parts := m.split('.')
m_path := m_parts.join('/')
if mod == m_parts[m_parts.len-1] && file_path.contains(m_path) {
return m
}
}
}
return mod
}
2019-09-01 22:51:16 +03:00
fn new_file_import_table(file_path string) &FileImportTable {
return &FileImportTable{
2019-07-12 08:37:54 +03:00
file_path: file_path
2019-08-17 22:19:37 +03:00
imports: map[string]string
2019-07-12 08:37:54 +03:00
}
}
fn (fit &FileImportTable) known_import(mod string) bool {
2019-07-23 23:57:06 +03:00
return mod in fit.imports || fit.is_aliased(mod)
2019-07-12 08:37:54 +03:00
}
2019-07-12 08:37:54 +03:00
fn (fit mut FileImportTable) register_import(mod string) {
fit.register_alias(mod, mod)
}
fn (fit mut FileImportTable) register_alias(alias string, mod string) {
2019-08-17 22:19:37 +03:00
if alias in fit.imports {
2019-08-29 03:30:17 +03:00
cerror('cannot import $mod as $alias: import name $alias already in use in "${fit.file_path}".')
2019-07-26 19:02:58 +03:00
}
if mod.contains('.internal.') {
mod_parts := mod.split('.')
mut internal_mod_parts := []string
for part in mod_parts {
if part == 'internal' { break }
internal_mod_parts << part
}
internal_parent := internal_mod_parts.join('.')
if !fit.module_name.starts_with(internal_parent) {
2019-08-29 03:30:17 +03:00
cerror('module $mod can only be imported internally by libs.')
2019-07-26 19:02:58 +03:00
}
}
2019-07-14 12:01:32 +03:00
fit.imports[alias] = mod
2019-07-12 08:37:54 +03:00
}
fn (fit &FileImportTable) known_alias(alias string) bool {
2019-08-17 22:19:37 +03:00
return alias in fit.imports
2019-07-12 08:37:54 +03:00
}
fn (fit &FileImportTable) is_aliased(mod string) bool {
2019-08-17 22:19:37 +03:00
for _, val in fit.imports {
2019-07-14 12:01:32 +03:00
if val == mod {
2019-08-17 22:19:37 +03:00
return true
}
2019-07-12 08:37:54 +03:00
}
return false
}
fn (fit &FileImportTable) resolve_alias(alias string) string {
2019-07-23 23:57:06 +03:00
return fit.imports[alias]
2019-07-12 08:37:54 +03:00
}
2019-08-28 17:35:44 +03:00
fn (t &Type) contains_field_type(typ string) bool {
2019-08-29 01:52:32 +03:00
if !t.name[0].is_capital() {
return false
}
2019-08-28 17:35:44 +03:00
for field in t.fields {
if field.typ == typ {
return true
}
}
return false
}
// check for a function / variable / module typo in `name`
fn (table &Table) identify_typo(name string, current_fn &Fn, fit &FileImportTable) string {
// dont check if so short
if name.len < 2 { return '' }
min_match := 0.8 // for dice coefficient between 0.0 - 1.0
name_orig := name.replace('__', '.').replace('_dot_', '.')
mut output := ''
// check functions
mut n := table.find_misspelled_fn(name_orig, min_match)
if n != '' {
output += '\n * function: `$n`'
}
// check function local variables
n = current_fn.find_misspelled_local_var(name_orig, min_match)
if n != '' {
output += '\n * variable: `$n`'
}
// check imported modules
n = table.find_misspelled_imported_mod(name_orig, fit, min_match)
if n != '' {
output += '\n * module: `$n`'
}
return output
}
// find function with closest name to `name`
2019-09-13 16:15:30 +03:00
fn (table &Table) find_misspelled_fn(name string, min_match f32) string {
mut closest := f32(0)
mut closest_fn := ''
for _, f in table.fns {
n := '${f.mod}.$f.name'
if !name.starts_with(f.mod) || (n.len - name.len > 3 || name.len - n.len > 3) { continue }
p := strings.dice_coefficient(name, n)
if p > closest {
closest = p
closest_fn = n
}
}
2019-09-13 16:15:30 +03:00
return if closest >= min_match { closest_fn } else { '' }
}
// find imported module with closest name to `name`
2019-09-13 16:15:30 +03:00
fn (table &Table) find_misspelled_imported_mod(name string, fit &FileImportTable, min_match f32) string {
mut closest := f32(0)
mut closest_mod := ''
for alias, mod in fit.imports {
n := '${fit.module_name}.$alias'
if !name.starts_with(fit.module_name) || (n.len - name.len > 3 || name.len - n.len > 3) { continue }
p := strings.dice_coefficient(name, n)
if p > closest {
closest = p
closest_mod = '$alias ($mod)'
}
}
2019-09-13 16:15:30 +03:00
return if closest >= min_match { closest_mod } else { '' }
}