v/compiler/table.v

910 lines
20 KiB
Go
Raw Normal View History

2019-06-23 04:21:30 +02: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 20:20:28 +02:00
module main
2019-08-17 21:19:37 +02:00
import math
import strings
2019-06-22 20:20:28 +02:00
struct Table {
mut:
types []Type
consts []Var
2019-08-17 21:19:37 +02: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
flags []string // ['-framework Cocoa', '-lglfw3']
2019-08-24 13:35:05 +02:00
fn_cnt int //atomic
obfuscate bool
2019-06-22 20:20:28 +02:00
}
2019-07-29 18:21:36 +02:00
struct GenTable {
2019-08-17 21:19:37 +02:00
fn_name string
types []string
}
2019-07-29 18:21:36 +02:00
2019-07-12 07:37:54 +02:00
// Holds import information scoped to the parsed file
struct FileImportTable {
mut:
2019-07-21 17:53:35 +02:00
module_name string
file_path string
imports map[string]string
2019-07-12 07:37:54 +02:00
}
2019-06-22 20:20:28 +02:00
enum AccessMod {
2019-08-17 21:19:37 +02:00
private // private immutable
private_mut // private mutable
2019-07-29 18:21:36 +02: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 20:20:28 +02:00
}
enum TypeCategory {
struct_
func
interface_ // 2
enum_
union_
c_struct // 5
c_typedef
}
2019-06-22 20:20:28 +02:00
struct Type {
mut:
mod string
2019-06-22 20:20:28 +02:00
name string
cat TypeCategory
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-08-17 21:19:37 +02:00
enum_vals []string
gen_types []string
2019-06-22 20:20:28 +02: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 21:19:37 +02:00
gen_str bool // needs `.str()` method generation
2019-06-22 20:20:28 +02: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',
// '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 20:20:28 +02:00
]
2019-06-22 20:20:28 +02: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 18:21:36 +02:00
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 (
number_types = ['number', 'int', 'i8', 'u8', 'i16', 'u16', 'i32', 'u32', 'byte', 'i64', 'u64', 'f32', 'f64']
float_types = ['f32', 'f64']
2019-06-22 20:20:28 +02:00
)
fn is_number_type(typ string) bool {
2019-08-17 21:19:37 +02:00
return typ in number_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
2019-06-22 20:20:28 +02:00
fn new_table(obfuscate bool) *Table {
mut t := &Table {
2019-08-17 21:19:37 +02:00
obf_ids: map[string]int
fns: map[string]Fn
//generic_fns: map[string]GenTable{}
generic_fns: []GenTable
2019-06-22 20:20:28 +02:00
obfuscate: obfuscate
file_imports: []FileImportTable
2019-06-22 20:20:28 +02:00
}
t.register_type('int')
t.register_type('size_t')
t.register_type_with_parent('i8', 'int')
t.register_type_with_parent('u8', 'u32')
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('i32', 'int')
t.register_type_with_parent('u32', 'int')
t.register_type_with_parent('byte', 'int')
t.register_type_with_parent('i64', 'int')
t.register_type_with_parent('u64', 'u32')
2019-06-22 20:20:28 +02: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 18:21:36 +02:00
t.register_type('T')
2019-06-22 20:20:28 +02:00
t.register_type('va_list')
2019-08-22 22:19:31 +02: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 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 mut Table) var_cgen_name(name string) string {
if CReserved.contains(name) {
return 'v_$name'
}
else {
return name
}
}
fn (t mut Table) register_module(mod string) {
if t.modules.contains(mod) {
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-08-22 22:19:31 +02:00
fn (t mut Table) register_const(name, typ, mod string) {
2019-06-22 20:20:28 +02:00
t.consts << Var {
name: name
typ: typ
is_const: true
2019-08-17 21:19:37 +02:00
mod: mod
2019-06-22 20:20:28 +02: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 21:19:37 +02:00
mod: p.mod
is_mut: true
2019-06-22 20:20:28 +02:00
}
}
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(' ') {
typ = typ.left(typ.len - 1)
}
for t in table.types {
if t.name == typ && !t.is_placeholder {
return true
}
}
return false
}
fn (t &Table) find_fn(name string) Fn {
2019-08-17 21:19:37 +02:00
f := t.fns[name]
if !isnil(f.name.str) {
return f
}
2019-06-22 20:20:28 +02:00
return Fn{}
}
fn (t &Table) known_fn(name string) bool {
2019-08-17 21:19:37 +02:00
f := t.find_fn(name)
return f.name != ''
2019-06-22 20:20:28 +02: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
}
for typ2 in t.types {
if typ2.name == typ {
return
}
}
// if t.types.filter( _.name == typ.name).len > 0 {
// return
// }
t.types << Type {
2019-06-22 20:20:28 +02:00
name: typ
}
}
fn (p mut Parser) register_type_with_parent(strtyp, parent string) {
typ := Type {
name: strtyp
parent: parent
mod: p.mod
2019-06-22 20:20:28 +02:00
}
p.table.register_type2(typ)
}
fn (t mut Table) register_type_with_parent(typ, parent string) {
if typ.len == 0 {
return
}
// if t.types.filter(_.name == typ) > 0
for typ2 in t.types {
if typ2.name == typ {
return
}
}
/*
mut mod := ''
2019-06-22 20:20:28 +02:00
if parent == 'array' {
mod = 'builtin'
}
2019-06-22 20:20:28 +02:00
*/
2019-08-17 21:19:37 +02:00
t.types << Type {
2019-06-22 20:20:28 +02:00
name: typ
parent: parent
2019-08-17 21:19:37 +02:00
//mod: mod
2019-06-22 20:20:28 +02:00
}
}
fn (t mut Table) register_type2(typ Type) {
if typ.name.len == 0 {
return
}
for typ2 in t.types {
if typ2.name == typ.name {
return
}
}
t.types << typ
}
fn (t mut Type) add_field(name, typ string, is_mut bool, attr string, access_mod AccessMod) {
// if t.name == 'Parser' {
// println('adding field $name')
// }
v := Var {
name: name
typ: typ
is_mut: is_mut
attr: attr
access_mod: access_mod
}
t.fields << v
}
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 21:19:37 +02:00
return name in t.enum_vals
}
2019-06-22 20:20:28 +02: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
}
fn (t mut Type) add_method(f Fn) {
// if t.name.contains('Parser') {
// println('!!!add_method() $f.name to $t.name len=$t.methods.len cap=$t.methods.cap')
// }
t.methods << f
// println('end add_method()')
}
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')
method := typ.find_method(name)
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 21:19:37 +02:00
/*
2019-06-22 20:20:28 +02:00
fn (t mut Type) add_gen_type(type_name string) {
// println('add_gen_type($s)')
if t.gen_types.contains(type_name) {
return
}
t.gen_types << type_name
}
2019-08-17 21:19:37 +02:00
*/
2019-06-22 20:20:28 +02:00
2019-08-10 23:02:48 +02:00
fn (p &Parser) find_type(name string) &Type {
2019-06-22 20:20:28 +02:00
typ := p.table.find_type(name)
if typ.name.len == 0 {
return p.table.find_type(p.prepend_mod(name))
2019-06-22 20:20:28 +02:00
}
return typ
}
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(' ') {
name = name.left(name.len - 1)
}
// TODO PERF use map
for i, typ in t.types {
if typ.name == name {
return &t.types[i]
}
}
return &Type{}
}
fn (p mut Parser) _check_types(got_, expected_ string, throw bool) bool {
2019-08-17 21:19:37 +02:00
mut got := got_
mut expected := expected_
2019-06-22 20:20:28 +02:00
p.log('check types got="$got" exp="$expected" ')
if p.pref.translated {
2019-06-22 20:20:28 +02:00
return true
}
// 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-06-25 22:19:17 +02: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
if got=='void*' || expected=='void*' {// || got == 'cvoid' || expected == 'cvoid' {
// if !p.builtin_mod {
if p.pref.is_play {
2019-06-22 20:20:28 +02:00
return false
}
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 20:20:28 +02:00
return true
}
if got=='byteptr' && expected=='byte*' {
2019-06-25 23:43:04 +02:00
return true
}
if got=='byte*' && expected=='byteptr' {
return true
2019-08-17 21:19:37 +02:00
}
if got=='int' && expected=='byte*' {
2019-06-22 20:20:28 +02:00
return true
}
2019-08-03 09:44:08 +02:00
//if got=='int' && expected=='voidptr*' {
2019-08-17 21:19:37 +02:00
//return true
//}
2019-06-22 20:20:28 +02:00
// byteptr += int
if got=='int' && expected=='byteptr' {
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(got) {
return true
}
// NsColor* return 0
if !p.pref.is_play {
2019-06-22 20:20:28 +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
// }
// Allow pointer arithmetic
if expected=='void*' && got=='int' {
2019-06-22 20:20:28 +02:00
return true
}
}
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 {
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 02:02:33 +02: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 type_default(typ string) string {
if typ.starts_with('array_') {
return 'new_array(0, 1, sizeof( ${typ.right(6)} ))'
2019-06-22 20:20:28 +02:00
}
// Always set pointers to 0
if typ.ends_with('*') {
return '0'
}
2019-08-17 21:19:37 +02:00
// User struct defined in another module.
2019-06-22 20:20:28 +02:00
if typ.contains('__') {
return '{0}'
2019-06-22 20:20:28 +02: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 12:33:18 +02:00
case 'i8': return '0'
case 'i16': return '0'
case 'i32': return '0'
2019-07-18 04:32:49 +02:00
case 'i64': return '0'
2019-07-15 12:33:18 +02:00
case 'u8': return '0'
case 'u16': return '0'
case 'u32': return '0'
2019-07-18 04:32:49 +02:00
case 'u64': return '0'
2019-07-15 12:33:18 +02: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 20:20:28 +02:00
}
return '{0}'
2019-06-22 20:20:28 +02:00
}
// TODO PERF O(n)
fn (t &Table) is_interface(name string) bool {
for typ in t.types {
if typ.cat == .interface_ && typ.name == name {
2019-06-22 20:20:28 +02:00
return true
}
}
return false
}
// Do we have fn main()?
fn (t &Table) main_exists() bool {
2019-08-17 21:19:37 +02:00
for _, f in t.fns {
2019-06-22 20:20:28 +02:00
if f.name == 'main' {
return true
}
}
return false
}
// TODO use `?Var`
fn (t &Table) find_const(name string) Var {
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' && CReserved.contains(f.name) {
2019-06-22 20:20:28 +02: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 20:20:28 +02: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 10:01:55 +02:00
println('$old ==> $name')
2019-06-22 20:20:28 +02: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 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 {
2019-08-17 21:19:37 +02:00
x := val.int()
switch typ {
2019-08-17 21:19:37 +02:00
case 'byte', 'u8': return 0 <= x && x <= math.MaxU8
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
case 'int', 'i32': return math.MinI32 <= x && x <= math.MaxI32
//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 18:21:36 +02:00
for _, f in t.generic_fns {
if f.fn_name == fn_name {
return f.types
2019-08-17 21:19:37 +02:00
}
}
panic('function $fn_name not found')
2019-08-17 21:19:37 +02:00
}
2019-07-29 18:21:36 +02:00
// `foo<Bar>()`
// fn_name == 'foo'
2019-08-17 21:19:37 +02:00
// typ == 'Bar'
fn (t mut Table) register_generic_fn_type(fn_name, typ string) {
2019-07-29 18:21:36 +02:00
for i, f in t.generic_fns {
if f.fn_name == fn_name {
2019-08-17 21:19:37 +02:00
t.generic_fns[i].types << typ
return
}
}
}
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'
}
switch typ {
case 'string': return '%.*s'
2019-08-09 18:10:59 +02:00
//case 'bool': return '%.*s'
2019-07-29 18:21:36 +02:00
case 'ustring': return '%.*s'
case 'byte', 'bool', 'int', 'char', 'byte', 'i32', 'i16', 'i8': return '%d'
case 'u8', 'u16', 'u32': return '%u'
2019-07-29 18:21:36 +02:00
case 'f64', 'f32': return '%f'
case 'i64': return '%lld'
case 'u64': return '%llu'
2019-07-29 18:21:36 +02: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 21:19:37 +02:00
return '%p'
}
}
if t.parent != '' && level == 0 {
return p.typ_to_fmt(t.parent, level+1)
2019-07-29 18:21:36 +02:00
}
return ''
}
fn is_compile_time_const(s_ string) bool {
s := s_.trim_space()
2019-07-29 18:21:36 +02: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 07:37:54 +02: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
}
fn new_file_import_table(file_path string) *FileImportTable {
return &FileImportTable{
2019-07-12 07:37:54 +02:00
file_path: file_path
2019-08-17 21:19:37 +02:00
imports: map[string]string
2019-07-12 07:37:54 +02:00
}
}
fn (fit &FileImportTable) known_import(mod string) bool {
2019-07-23 22:57:06 +02:00
return mod in fit.imports || fit.is_aliased(mod)
2019-07-12 07:37:54 +02:00
}
2019-07-12 07:37:54 +02: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 21:19:37 +02:00
if alias in fit.imports {
2019-07-14 11:01:32 +02:00
panic('cannot import $mod as $alias: import name $alias already in use in "${fit.file_path}".')
2019-07-26 18:02:58 +02: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) {
panic('module $mod can only be imported internally by libs.')
}
}
2019-07-14 11:01:32 +02:00
fit.imports[alias] = mod
2019-07-12 07:37:54 +02:00
}
fn (fit &FileImportTable) known_alias(alias string) bool {
2019-08-17 21:19:37 +02:00
return alias in fit.imports
2019-07-12 07:37:54 +02:00
}
fn (fit &FileImportTable) is_aliased(mod string) bool {
2019-08-17 21:19:37 +02:00
for _, val in fit.imports {
2019-07-14 11:01:32 +02:00
if val == mod {
2019-08-17 21:19:37 +02:00
return true
}
2019-07-12 07:37:54 +02:00
}
return false
}
fn (fit &FileImportTable) resolve_alias(alias string) string {
2019-07-23 22:57:06 +02:00
return fit.imports[alias]
2019-07-12 07:37:54 +02:00
}