mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
run vfmt on table.v
This commit is contained in:
parent
1679457f6b
commit
ce86626ec2
@ -8,6 +8,7 @@ fn (p mut Parser) enum_decl(no_name bool) {
|
|||||||
is_pub := p.tok == .key_pub
|
is_pub := p.tok == .key_pub
|
||||||
if is_pub {
|
if is_pub {
|
||||||
p.next()
|
p.next()
|
||||||
|
p.fspace()
|
||||||
}
|
}
|
||||||
p.check(.key_enum)
|
p.check(.key_enum)
|
||||||
p.fspace()
|
p.fspace()
|
||||||
|
@ -46,6 +46,7 @@ fn (p mut Parser) match_statement(is_expr bool) string {
|
|||||||
// allow braces is else
|
// allow braces is else
|
||||||
got_brace := p.tok == .lcbr
|
got_brace := p.tok == .lcbr
|
||||||
if got_brace {
|
if got_brace {
|
||||||
|
p.fspace()
|
||||||
p.check(.lcbr)
|
p.check(.lcbr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +74,8 @@ fn (p mut Parser) match_statement(is_expr bool) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if is_expr {
|
if is_expr {
|
||||||
// statements are dissallowed (if match is expression) so user cant declare variables there and so on
|
// statements are dissallowed (if match is expression) so
|
||||||
|
// user cant declare variables there and so on
|
||||||
p.gen(':(')
|
p.gen(':(')
|
||||||
|
|
||||||
// allow braces is else
|
// allow braces is else
|
||||||
@ -185,6 +187,7 @@ fn (p mut Parser) match_statement(is_expr bool) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// braces are required for now
|
// braces are required for now
|
||||||
|
p.fgen_nl()
|
||||||
p.check(.rcbr)
|
p.check(.rcbr)
|
||||||
|
|
||||||
p.gen(')')
|
p.gen(')')
|
||||||
|
@ -419,14 +419,14 @@ fn (p mut Parser) parse(pass Pass) {
|
|||||||
p.mod = p.check_name()
|
p.mod = p.check_name()
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
p.fgenln('\n')
|
p.fgen_nl()
|
||||||
p.cgen.nogen = false
|
p.cgen.nogen = false
|
||||||
if p.pref.build_mode == .build_module && p.mod != p.v.mod {
|
if p.pref.build_mode == .build_module && p.mod != p.v.mod {
|
||||||
// println('skipping $p.mod (v.mod = $p.v.mod)')
|
// println('skipping $p.mod (v.mod = $p.v.mod)')
|
||||||
p.cgen.nogen = true
|
p.cgen.nogen = true
|
||||||
// defer { p.cgen.nogen = false }
|
// defer { p.cgen.nogen = false }
|
||||||
}
|
}
|
||||||
p.fgenln('\n')
|
p.fgen_nl()
|
||||||
p.builtin_mod = p.mod == 'builtin'
|
p.builtin_mod = p.mod == 'builtin'
|
||||||
p.can_chash = p.mod in ['ui', 'darwin', 'clipboard', 'webview'] // TODO tmp remove
|
p.can_chash = p.mod in ['ui', 'darwin', 'clipboard', 'webview'] // TODO tmp remove
|
||||||
// Import pass - the first and the smallest pass that only analyzes imports
|
// Import pass - the first and the smallest pass that only analyzes imports
|
||||||
@ -466,7 +466,6 @@ fn (p mut Parser) parse(pass Pass) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.key_pub {
|
.key_pub {
|
||||||
p.fspace()
|
|
||||||
next := p.peek()
|
next := p.peek()
|
||||||
match next {
|
match next {
|
||||||
.key_fn {
|
.key_fn {
|
||||||
@ -522,6 +521,7 @@ fn (p mut Parser) parse(pass Pass) {
|
|||||||
p.next()
|
p.next()
|
||||||
p.fspace()
|
p.fspace()
|
||||||
name := p.check_name()
|
name := p.check_name()
|
||||||
|
p.fspace()
|
||||||
typ := p.get_type()
|
typ := p.get_type()
|
||||||
p.register_global(name, typ)
|
p.register_global(name, typ)
|
||||||
// p.genln(p.table.cgen_name_type_pair(name, typ))
|
// p.genln(p.table.cgen_name_type_pair(name, typ))
|
||||||
@ -665,6 +665,7 @@ fn (p mut Parser) const_decl() {
|
|||||||
is_pub := p.tok == .key_pub
|
is_pub := p.tok == .key_pub
|
||||||
if is_pub {
|
if is_pub {
|
||||||
p.next()
|
p.next()
|
||||||
|
p.fspace()
|
||||||
}
|
}
|
||||||
p.inside_const = true
|
p.inside_const = true
|
||||||
p.check(.key_const)
|
p.check(.key_const)
|
||||||
@ -770,9 +771,12 @@ fn (p mut Parser) type_decl() {
|
|||||||
is_pub := p.tok == .key_pub
|
is_pub := p.tok == .key_pub
|
||||||
if is_pub {
|
if is_pub {
|
||||||
p.next()
|
p.next()
|
||||||
|
p.fspace()
|
||||||
}
|
}
|
||||||
p.check(.key_type)
|
p.check(.key_type)
|
||||||
|
p.fspace()
|
||||||
name := p.check_name()
|
name := p.check_name()
|
||||||
|
p.fspace()
|
||||||
// V used to have 'type Foo struct', many Go users might use this syntax
|
// V used to have 'type Foo struct', many Go users might use this syntax
|
||||||
if p.tok == .key_struct {
|
if p.tok == .key_struct {
|
||||||
p.error('use `struct $name {` instead of `type $name struct {`')
|
p.error('use `struct $name {` instead of `type $name struct {`')
|
||||||
@ -867,7 +871,7 @@ fn (p &Parser) strtok() string {
|
|||||||
return '`$p.lit`'
|
return '`$p.lit`'
|
||||||
}
|
}
|
||||||
if p.tok == .str {
|
if p.tok == .str {
|
||||||
if p.lit.contains("'") {
|
if p.lit.contains("'") && !p.lit.contains('"') {
|
||||||
return '"$p.lit"'
|
return '"$p.lit"'
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2989,3 +2993,4 @@ fn (p &Parser) is_expr_fn_call(start_tok_idx int) (bool,string) {
|
|||||||
fn todo_remove() {
|
fn todo_remove() {
|
||||||
x64.new_gen('f')
|
x64.new_gen('f')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||||
// Use of this source code is governed by an MIT license
|
// Use of this source code is governed by an MIT license
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
|
|
||||||
module compiler
|
module compiler
|
||||||
|
|
||||||
import strings
|
import strings
|
||||||
|
|
||||||
struct Table {
|
struct Table {
|
||||||
pub mut:
|
pub mut:
|
||||||
typesmap map[string]Type
|
typesmap map[string]Type
|
||||||
consts []Var
|
consts []Var
|
||||||
fns map[string]Fn
|
fns map[string]Fn
|
||||||
obf_ids map[string]int // obf_ids['myfunction'] == 23
|
obf_ids map[string]int // obf_ids['myfunction'] == 23
|
||||||
modules []string // List of all modules registered by the application
|
modules []string // List of all modules registered by the application
|
||||||
imports []string // List of all imports
|
imports []string // List of all imports
|
||||||
cflags []CFlag // ['-framework Cocoa', '-lglfw3']
|
cflags []CFlag // ['-framework Cocoa', '-lglfw3']
|
||||||
fn_cnt int //atomic
|
fn_cnt int // atomic
|
||||||
obfuscate bool
|
obfuscate bool
|
||||||
varg_access []VargAccess
|
varg_access []VargAccess
|
||||||
//enum_vals map[string][]string
|
// enum_vals map[string][]string
|
||||||
//names []Name
|
// names []Name
|
||||||
max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12
|
max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,11 +41,11 @@ struct Name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum AccessMod {
|
enum AccessMod {
|
||||||
private // private immutable
|
private // private immutable
|
||||||
private_mut // private mutable
|
private_mut // private mutable
|
||||||
public // public immutable (readonly)
|
public // public immutable (readonly)
|
||||||
public_mut // public, but mutable only in this module
|
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)
|
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 {
|
fn (a []AccessMod) contains(b AccessMod) bool {
|
||||||
@ -74,31 +73,31 @@ enum TypeCategory {
|
|||||||
|
|
||||||
struct Var {
|
struct Var {
|
||||||
pub mut:
|
pub mut:
|
||||||
typ string
|
typ string
|
||||||
name string
|
name string
|
||||||
idx int // index in the local_vars array
|
idx int // index in the local_vars array
|
||||||
is_arg bool
|
is_arg bool
|
||||||
is_const bool
|
is_const bool
|
||||||
args []Var // function args
|
args []Var // function args
|
||||||
attr string // [json] etc
|
attr string // [json] etc
|
||||||
is_mut bool
|
is_mut bool
|
||||||
is_alloc bool
|
is_alloc bool
|
||||||
is_returned bool
|
is_returned bool
|
||||||
ptr bool
|
ptr bool
|
||||||
ref bool
|
ref bool
|
||||||
parent_fn string // Variables can only be defined in functions
|
parent_fn string // Variables can only be defined in functions
|
||||||
mod string // module where this var is stored
|
mod string // module where this var is stored
|
||||||
access_mod AccessMod
|
access_mod AccessMod
|
||||||
is_global bool // __global (translated from C only)
|
is_global bool // __global (translated from C only)
|
||||||
is_used bool
|
is_used bool
|
||||||
is_changed bool
|
is_changed bool
|
||||||
scope_level int
|
scope_level int
|
||||||
is_c bool // todo remove once `typ` is `Type`, not string
|
is_c bool // todo remove once `typ` is `Type`, not string
|
||||||
is_moved bool
|
is_moved bool
|
||||||
line_nr int
|
line_nr int
|
||||||
token_idx int // this is a token index, which will be used by error reporting
|
token_idx int // this is a token index, which will be used by error reporting
|
||||||
is_for_var bool
|
is_for_var bool
|
||||||
is_public bool // for consts
|
is_public bool // for consts
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Type {
|
struct Type {
|
||||||
@ -112,22 +111,22 @@ pub mut:
|
|||||||
parent string
|
parent string
|
||||||
func Fn // For cat == FN (type myfn fn())
|
func Fn // For cat == FN (type myfn fn())
|
||||||
is_c bool // `C.FILE`
|
is_c bool // `C.FILE`
|
||||||
enum_vals []string
|
enum_vals []string
|
||||||
gen_types []string
|
gen_types []string
|
||||||
default_vals []string // `struct Foo { bar int = 2 }`
|
default_vals []string // `struct Foo { bar int = 2 }`
|
||||||
// `is_placeholder` is used for types that are not defined yet but are known to exist.
|
// `is_placeholder` 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.
|
// It allows having things like `fn (f Foo) bar()` before `Foo` is defined.
|
||||||
// This information is needed in the first pass.
|
// This information is needed in the first pass.
|
||||||
is_placeholder bool
|
is_placeholder bool
|
||||||
gen_str bool // needs `.str()` method generation
|
gen_str bool // needs `.str()` method generation
|
||||||
is_flag bool // enum bitfield flag
|
is_flag bool // enum bitfield flag
|
||||||
//max_field_len int
|
// max_field_len int
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypeNode {
|
struct TypeNode {
|
||||||
mut:
|
mut:
|
||||||
next &TypeNode
|
next &TypeNode
|
||||||
typ Type
|
typ Type
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -153,50 +152,19 @@ pub fn (t Type) str() string {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
c_reserved = [
|
c_reserved = ['delete', 'exit', 'unix',
|
||||||
'delete',
|
// 'print',
|
||||||
'exit',
|
// 'ok',
|
||||||
'unix',
|
'error', 'malloc', 'calloc', 'free', 'panic',
|
||||||
//'print',
|
// Full list of C reserved words, from: https://en.cppreference.com/w/c/keyword
|
||||||
// 'ok',
|
'auto', 'char', 'default', 'do', 'double', 'extern', 'float', 'inline', 'int', 'long', 'register', 'restrict', 'short', 'signed', 'sizeof', 'static', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while', ]
|
||||||
'error',
|
|
||||||
'malloc',
|
|
||||||
'calloc',
|
|
||||||
'free',
|
|
||||||
'panic',
|
|
||||||
|
|
||||||
// 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',
|
|
||||||
]
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is used for debugging only
|
// This is used for debugging only
|
||||||
pub fn (f Fn) str() string {
|
pub fn (f Fn) str() string {
|
||||||
t := Table{}
|
t := Table{
|
||||||
|
}
|
||||||
str_args := f.str_args(t)
|
str_args := f.str_args(t)
|
||||||
return '${f.name}($str_args) $f.typ'
|
return '${f.name}($str_args) $f.typ'
|
||||||
}
|
}
|
||||||
@ -213,7 +181,7 @@ pub fn (t &Table) debug_fns() string {
|
|||||||
// }
|
// }
|
||||||
const (
|
const (
|
||||||
integer_types = ['int', 'i8', 'byte', 'i16', 'u16', 'u32', 'i64', 'u64']
|
integer_types = ['int', 'i8', 'byte', 'i16', 'u16', 'u32', 'i64', 'u64']
|
||||||
float_types = ['f32', 'f64']
|
float_types = ['f32', 'f64']
|
||||||
reserved_type_param_names = ['R', 'S', 'T', 'U', 'W']
|
reserved_type_param_names = ['R', 'S', 'T', 'U', 'W']
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -241,10 +209,12 @@ fn (t mut Table) register_enum_val(typ, val string) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
fn new_table(obfuscate bool) &Table {
|
fn new_table(obfuscate bool) &Table {
|
||||||
mut t := &Table {
|
mut t := &Table{
|
||||||
obfuscate: obfuscate
|
obfuscate: obfuscate
|
||||||
//enum_vals: map[string][]string
|
// enum_vals: map[string][]string
|
||||||
|
|
||||||
}
|
}
|
||||||
t.register_builtin('int')
|
t.register_builtin('int')
|
||||||
t.register_builtin('size_t')
|
t.register_builtin('size_t')
|
||||||
@ -334,7 +304,7 @@ fn (t mut Table) register_const(name, typ, mod string, is_pub bool) {
|
|||||||
|
|
||||||
// Only for translated code
|
// Only for translated code
|
||||||
fn (p mut Parser) register_global(name, typ string) {
|
fn (p mut Parser) register_global(name, typ string) {
|
||||||
p.table.consts << Var {
|
p.table.consts << Var{
|
||||||
name: name
|
name: name
|
||||||
typ: typ
|
typ: typ
|
||||||
is_const: true
|
is_const: true
|
||||||
@ -367,7 +337,8 @@ fn (table &Table) known_type_fast(t &Type) bool {
|
|||||||
|
|
||||||
fn (t &Table) find_fn(name string) ?Fn {
|
fn (t &Table) find_fn(name string) ?Fn {
|
||||||
f := t.fns[name]
|
f := t.fns[name]
|
||||||
if f.name.str != 0 { // TODO
|
if f.name.str != 0 {
|
||||||
|
// TODO
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
return none
|
return none
|
||||||
@ -375,7 +346,8 @@ fn (t &Table) find_fn(name string) ?Fn {
|
|||||||
|
|
||||||
fn (t &Table) find_fn_is_script(name string, is_script bool) ?Fn {
|
fn (t &Table) find_fn_is_script(name string, is_script bool) ?Fn {
|
||||||
mut f := t.fns[name]
|
mut f := t.fns[name]
|
||||||
if f.name.str != 0 { // TODO
|
if f.name.str != 0 {
|
||||||
|
// TODO
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
// V script? Try os module.
|
// V script? Try os module.
|
||||||
@ -390,12 +362,16 @@ fn (t &Table) find_fn_is_script(name string, is_script bool) ?Fn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (t &Table) known_fn(name string) bool {
|
fn (t &Table) known_fn(name string) bool {
|
||||||
_ = t.find_fn(name) or { return false }
|
_ = t.find_fn(name) or {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (t &Table) known_const(name string) bool {
|
fn (t &Table) known_const(name string) bool {
|
||||||
_ = t.find_const(name) or { return false }
|
_ = t.find_const(name) or {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,11 +382,14 @@ fn (t mut Table) register_builtin(typ string) {
|
|||||||
if typ in t.typesmap {
|
if typ in t.typesmap {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.typesmap[typ] = Type{name:typ, is_public:true}
|
t.typesmap[typ] = Type{
|
||||||
|
name: typ
|
||||||
|
is_public: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) register_type_with_parent(strtyp, parent string) {
|
fn (p mut Parser) register_type_with_parent(strtyp, parent string) {
|
||||||
typ := Type {
|
typ := Type{
|
||||||
name: strtyp
|
name: strtyp
|
||||||
parent: parent
|
parent: parent
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
@ -423,11 +402,12 @@ fn (t mut Table) register_type_with_parent(typ, parent string) {
|
|||||||
if typ.len == 0 {
|
if typ.len == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.typesmap[typ] = Type {
|
t.typesmap[typ] = Type{
|
||||||
name: typ
|
name: typ
|
||||||
parent: parent
|
parent: parent
|
||||||
is_public: true
|
is_public: true
|
||||||
//mod: mod
|
// mod: mod
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,7 +422,7 @@ fn (t mut Table) rewrite_type(typ Type) {
|
|||||||
if typ.name.len == 0 {
|
if typ.name.len == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.typesmap[typ.name] = typ
|
t.typesmap[typ.name] = typ
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (table mut Table) add_field(type_name, field_name, field_type string, is_mut bool, attr string, access_mod AccessMod) {
|
fn (table mut Table) add_field(type_name, field_name, field_type string, is_mut bool, attr string, access_mod AccessMod) {
|
||||||
@ -451,12 +431,13 @@ fn (table mut Table) add_field(type_name, field_name, field_type string, is_mut
|
|||||||
verror('add_field: empty type')
|
verror('add_field: empty type')
|
||||||
}
|
}
|
||||||
mut t := table.typesmap[type_name]
|
mut t := table.typesmap[type_name]
|
||||||
t.fields << Var {
|
t.fields << Var{
|
||||||
name: field_name
|
name: field_name
|
||||||
typ: field_type
|
typ: field_type
|
||||||
is_mut: is_mut
|
is_mut: is_mut
|
||||||
attr: attr
|
attr: attr
|
||||||
parent_fn: type_name // Name of the parent type
|
parent_fn: type_name // Name of the parent type
|
||||||
|
|
||||||
access_mod: access_mod
|
access_mod: access_mod
|
||||||
}
|
}
|
||||||
table.typesmap[type_name] = t
|
table.typesmap[type_name] = t
|
||||||
@ -472,7 +453,9 @@ fn (table mut Table) add_default_val(idx int, type_name, val_expr string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (t &Type) has_field(name string) bool {
|
fn (t &Type) has_field(name string) bool {
|
||||||
_ = t.find_field(name) or { return false }
|
_ = t.find_field(name) or {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,7 +473,9 @@ fn (t &Type) find_field(name string) ?Var {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (table &Table) type_has_field(typ &Type, name string) bool {
|
fn (table &Table) type_has_field(typ &Type, name string) bool {
|
||||||
_ = table.find_field(typ, name) or { return false }
|
_ = table.find_field(typ, name) or {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,12 +514,16 @@ fn (p mut Parser) add_method(type_name string, f Fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (t &Type) has_method(name string) bool {
|
fn (t &Type) has_method(name string) bool {
|
||||||
_ = t.find_method(name) or { return false }
|
_ = t.find_method(name) or {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (table &Table) type_has_method(typ &Type, name string) bool {
|
fn (table &Table) type_has_method(typ &Type, name string) bool {
|
||||||
_ = table.find_method(typ, name) or { return false }
|
_ = table.find_method(typ, name) or {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,8 +580,9 @@ fn (t &Table) find_type(name_ string) Type {
|
|||||||
name = name.replace('*', '')
|
name = name.replace('*', '')
|
||||||
}
|
}
|
||||||
if !(name in t.typesmap) {
|
if !(name in t.typesmap) {
|
||||||
//println('ret Type')
|
// println('ret Type')
|
||||||
return Type{}
|
return Type{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return t.typesmap[name]
|
return t.typesmap[name]
|
||||||
}
|
}
|
||||||
@ -600,14 +590,13 @@ fn (t &Table) find_type(name_ string) Type {
|
|||||||
fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
||||||
mut got := got_
|
mut got := got_
|
||||||
mut expected := expected_
|
mut expected := expected_
|
||||||
//p.log('check types got="$got" exp="$expected" ')
|
// p.log('check types got="$got" exp="$expected" ')
|
||||||
if p.pref.translated {
|
if p.pref.translated {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if got == expected {
|
if got == expected {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// generic return type
|
// generic return type
|
||||||
if expected == '_ANYTYPE_' {
|
if expected == '_ANYTYPE_' {
|
||||||
p.cur_fn.typ = got
|
p.cur_fn.typ = got
|
||||||
@ -637,7 +626,7 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Allow ints to be used as longs
|
// Allow ints to be used as longs
|
||||||
if got=='int' && expected=='i64' {
|
if got == 'int' && expected == 'i64' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if got == 'void*' && expected.starts_with('fn ') {
|
if got == 'void*' && expected.starts_with('fn ') {
|
||||||
@ -647,34 +636,35 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Todo void* allows everything right now
|
// Todo void* allows everything right now
|
||||||
if got=='void*' || expected=='void*' {// || got == 'cvoid' || expected == 'cvoid' {
|
if got == 'void*' || expected == 'void*' {
|
||||||
|
// || got == 'cvoid' || expected == 'cvoid' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// TODO only allow numeric consts to be assigned to bytes, and
|
// TODO only allow numeric consts to be assigned to bytes, and
|
||||||
// throw an error if they are bigger than 255
|
// throw an error if they are bigger than 255
|
||||||
if got=='int' && expected=='byte' {
|
if got == 'int' && expected == 'byte' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if got=='byteptr' && expected=='byte*' {
|
if got == 'byteptr' && expected == 'byte*' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if got=='byte*' && expected=='byteptr' {
|
if got == 'byte*' && expected == 'byteptr' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if got=='charptr' && expected=='char*' {
|
if got == 'charptr' && expected == 'char*' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if got=='char*' && expected=='charptr' {
|
if got == 'char*' && expected == 'charptr' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if got=='int' && expected=='byte*' {
|
if got == 'int' && expected == 'byte*' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
//if got=='int' && expected=='voidptr*' {
|
// if got=='int' && expected=='voidptr*' {
|
||||||
//return true
|
// return true
|
||||||
//}
|
// }
|
||||||
// byteptr += int
|
// byteptr += int
|
||||||
if got=='int' && expected in ['byteptr', 'charptr'] {
|
if got == 'int' && expected in ['byteptr', 'charptr'] {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if got == 'Option' && expected.starts_with('Option_') {
|
if got == 'Option' && expected.starts_with('Option_') {
|
||||||
@ -699,32 +689,30 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
|||||||
// return true
|
// return true
|
||||||
// }
|
// }
|
||||||
// TODO fn hack
|
// TODO fn hack
|
||||||
if got.starts_with('fn ') && (expected.ends_with('fn') ||
|
if got.starts_with('fn ') && (expected.ends_with('fn') || expected.ends_with('Fn')) {
|
||||||
expected.ends_with('Fn')) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Allow pointer arithmetic
|
// Allow pointer arithmetic
|
||||||
if expected=='void*' && got=='int' {
|
if expected == 'void*' && got == 'int' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
//if p.fileis('_test') && is_number_type(got) && is_number_type(expected) {
|
// if p.fileis('_test') && is_number_type(got) && is_number_type(expected) {
|
||||||
//p.warn('got=$got exp=$expected $p.is_const_literal')
|
// p.warn('got=$got exp=$expected $p.is_const_literal')
|
||||||
//}
|
// }
|
||||||
// Allow `myu64 == 1`, `myfloat == 2` etc
|
// Allow `myu64 == 1`, `myfloat == 2` etc
|
||||||
if is_integer_type(got) && is_number_type(expected) && p.is_const_literal {
|
if is_integer_type(got) && is_number_type(expected) && p.is_const_literal {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if expected == 'integer' {
|
if expected == 'integer' {
|
||||||
if is_integer_type(got) {
|
if is_integer_type(got) {
|
||||||
return true
|
return true
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
p.error('expected type `$expected`, but got `$got`')
|
p.error('expected type `$expected`, but got `$got`')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = expected.replace('*', '')
|
expected = expected.replace('*', '')
|
||||||
got = got.replace('*', '').replace('ptr','')
|
got = got.replace('*', '').replace('ptr', '')
|
||||||
if got != expected {
|
if got != expected {
|
||||||
// Interface check
|
// Interface check
|
||||||
if expected.ends_with('er') {
|
if expected.ends_with('er') {
|
||||||
@ -742,17 +730,19 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) base_type(name string) string {
|
fn (p mut Parser) base_type(name string) string {
|
||||||
typ := p.find_type(name)
|
typ := p.find_type(name)
|
||||||
if typ.parent != '' {
|
if typ.parent != '' {
|
||||||
return p.base_type(typ.parent)
|
return p.base_type(typ.parent)
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
// throw by default
|
// throw by default
|
||||||
fn (p mut Parser) check_types(got, expected string) bool {
|
fn (p mut Parser) check_types(got, expected string) bool {
|
||||||
if p.first_pass() { return true }
|
if p.first_pass() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return p.check_types2(got, expected, true)
|
return p.check_types2(got, expected, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,8 +762,7 @@ fn (p mut Parser) satisfies_interface(interface_name, _typ string, throw bool) b
|
|||||||
for method in int_typ.methods {
|
for method in int_typ.methods {
|
||||||
if !typ.has_method(method.name) {
|
if !typ.has_method(method.name) {
|
||||||
// if throw {
|
// if throw {
|
||||||
p.error('type `$_typ` doesn\'t satisfy interface ' +
|
p.error("type `$_typ` doesn\'t satisfy interface " + '`$interface_name` (method `$method.name` is not implemented)')
|
||||||
'`$interface_name` (method `$method.name` is not implemented)')
|
|
||||||
// }
|
// }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -781,7 +770,6 @@ fn (p mut Parser) satisfies_interface(interface_name, _typ string, throw bool) b
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn (table &Table) is_interface(name string) bool {
|
fn (table &Table) is_interface(name string) bool {
|
||||||
if !(name in table.typesmap) {
|
if !(name in table.typesmap) {
|
||||||
return false
|
return false
|
||||||
@ -811,7 +799,7 @@ fn (t &Table) all_test_function_names() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (t &Table) find_const(name string) ?Var {
|
fn (t &Table) find_const(name string) ?Var {
|
||||||
//println('find const l=$t.consts.len')
|
// println('find const l=$t.consts.len')
|
||||||
for c in t.consts {
|
for c in t.consts {
|
||||||
if c.name == name {
|
if c.name == name {
|
||||||
return c
|
return c
|
||||||
@ -850,21 +838,28 @@ fn (table &Table) cgen_name_type_pair(name, typ string) string {
|
|||||||
fn is_valid_int_const(val, typ string) bool {
|
fn is_valid_int_const(val, typ string) bool {
|
||||||
x := val.int()
|
x := val.int()
|
||||||
return match typ {
|
return match typ {
|
||||||
'byte' { 0 <= x && x <= 255 }
|
'byte'{
|
||||||
'u16' { 0 <= x && x <= 65535 }
|
0 <= x && x <= 255
|
||||||
//case 'u32': return 0 <= x && x <= math.MaxU32
|
}
|
||||||
//case 'u64': return 0 <= x && x <= math.MaxU64
|
'u16'{
|
||||||
//////////////
|
0 <= x && x <= 65535
|
||||||
'i8' { -128 <= x && x <= 127 }
|
}
|
||||||
/*
|
// case 'u32': return 0 <= x && x <= math.MaxU32
|
||||||
|
// case 'u64': return 0 <= x && x <= math.MaxU64
|
||||||
|
// ////////////
|
||||||
|
'i8'{
|
||||||
|
-128 <= x && x <= 127
|
||||||
|
}
|
||||||
|
/*
|
||||||
case 'i16': return math.min_i16 <= x && x <= math.max_i16
|
case 'i16': return math.min_i16 <= x && x <= math.max_i16
|
||||||
case 'int': return math.min_i32 <= x && x <= math.max_i32
|
case 'int': return math.min_i32 <= x && x <= math.max_i32
|
||||||
*/
|
*/
|
||||||
//case 'i64':
|
|
||||||
//x64 := val.i64()
|
// case 'i64':
|
||||||
//return i64(-(1<<63)) <= x64 && x64 <= i64((1<<63)-1)
|
// x64 := val.i64()
|
||||||
else { true }
|
// return i64(-(1<<63)) <= x64 && x64 <= i64((1<<63)-1)
|
||||||
}
|
else {
|
||||||
|
true}}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
||||||
@ -873,32 +868,49 @@ fn (p mut Parser) typ_to_fmt(typ string, level int) string {
|
|||||||
return '%d'
|
return '%d'
|
||||||
}
|
}
|
||||||
match typ {
|
match typ {
|
||||||
'string' { return '%.*s'}
|
'string' {
|
||||||
//case 'bool': return '%.*s'
|
return '%.*s'
|
||||||
'ustring' { return '%.*s'}
|
}
|
||||||
'byte', 'bool', 'int', 'char', 'byte', 'i16', 'i8' { return '%d'}
|
// case 'bool': return '%.*s'
|
||||||
'u16', 'u32' { return '%u'}
|
'ustring' {
|
||||||
'f64', 'f32' { return '%f'}
|
return '%.*s'
|
||||||
'i64' { return '%lld'}
|
}
|
||||||
'u64' { return '%llu'}
|
'byte','bool','int','char','byte','i16','i8' {
|
||||||
'byte*', 'byteptr' { return '%s'}
|
return '%d'
|
||||||
// case 'array_string': return '%s'
|
}
|
||||||
// case 'array_int': return '%s'
|
'u16','u32' {
|
||||||
'void' { p.error('cannot interpolate this value')}
|
return '%u'
|
||||||
|
}
|
||||||
|
'f64','f32' {
|
||||||
|
return '%f'
|
||||||
|
}
|
||||||
|
'i64' {
|
||||||
|
return '%lld'
|
||||||
|
}
|
||||||
|
'u64' {
|
||||||
|
return '%llu'
|
||||||
|
}
|
||||||
|
'byte*','byteptr' {
|
||||||
|
return '%s'
|
||||||
|
}
|
||||||
|
// case 'array_string': return '%s'
|
||||||
|
// case 'array_int': return '%s'
|
||||||
|
'void' {
|
||||||
|
p.error('cannot interpolate this value')
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if typ.ends_with('*') {
|
if typ.ends_with('*') {
|
||||||
return '%p'
|
return '%p'
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}
|
|
||||||
if t.parent != '' && level == 0 {
|
if t.parent != '' && level == 0 {
|
||||||
return p.typ_to_fmt(t.parent, level+1)
|
return p.typ_to_fmt(t.parent, level + 1)
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_to_safe_str(typ string) string {
|
fn type_to_safe_str(typ string) string {
|
||||||
r := typ.replace(' ','').replace('(','_').replace(')','_')
|
r := typ.replace(' ', '').replace('(', '_').replace(')', '_')
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -907,11 +919,11 @@ fn is_compile_time_const(s_ string) bool {
|
|||||||
if s == '' {
|
if s == '' {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if s.contains('\'') {
|
if s.contains("\'") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for c in s {
|
for c in s {
|
||||||
if ! ((c >= `0` && c <= `9`) || c == `.`) {
|
if !((c >= `0` && c <= `9`) || c == `.`) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -933,7 +945,9 @@ fn (t &Type) contains_field_type(typ string) bool {
|
|||||||
// check for a function / variable / module typo in `name`
|
// check for a function / variable / module typo in `name`
|
||||||
fn (p &Parser) identify_typo(name string) string {
|
fn (p &Parser) identify_typo(name string) string {
|
||||||
// dont check if so short
|
// dont check if so short
|
||||||
if name.len < 2 { return '' }
|
if name.len < 2 {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
name_dotted := mod_gen_name_rev(name.replace('__', '.'))
|
name_dotted := mod_gen_name_rev(name.replace('__', '.'))
|
||||||
min_match := 0.50 // for dice coefficient between 0.0 - 1.0
|
min_match := 0.50 // for dice coefficient between 0.0 - 1.0
|
||||||
mut output := ''
|
mut output := ''
|
||||||
@ -948,7 +962,7 @@ fn (p &Parser) identify_typo(name string) string {
|
|||||||
output += '\n * const: `$n`'
|
output += '\n * const: `$n`'
|
||||||
}
|
}
|
||||||
// check types
|
// check types
|
||||||
typ, type_cat := p.table.find_misspelled_type(name, p, min_match)
|
typ,type_cat := p.table.find_misspelled_type(name, p, min_match)
|
||||||
if typ.len > 0 {
|
if typ.len > 0 {
|
||||||
output += '\n * $type_cat: `$typ`'
|
output += '\n * $type_cat: `$typ`'
|
||||||
}
|
}
|
||||||
@ -967,13 +981,21 @@ fn (p &Parser) identify_typo(name string) string {
|
|||||||
|
|
||||||
// compare just name part, some items are mod prefied
|
// compare just name part, some items are mod prefied
|
||||||
fn typo_compare_name_mod(a, b, b_mod string) f32 {
|
fn typo_compare_name_mod(a, b, b_mod string) f32 {
|
||||||
if a.len - b.len > 2 || b.len - a.len > 2 { return 0 }
|
if a.len - b.len > 2 || b.len - a.len > 2 {
|
||||||
auidx := a.index('__') or { return 0 } // TODO or {-1} once cgen lines bug is fixed //-1 }
|
return 0
|
||||||
buidx := b.index('__') or { return 0 } //-1 }
|
}
|
||||||
|
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 { '' }
|
a_mod := if auidx != -1 { mod_gen_name_rev(a[..auidx]) } else { '' }
|
||||||
a_name := if auidx != -1 { a[auidx+2..] } else { a }
|
a_name := if auidx != -1 { a[auidx + 2..] } else { a }
|
||||||
b_name := if buidx != -1 { b[buidx+2..] } else { b }
|
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 }
|
if a_mod.len > 0 && b_mod.len > 0 && a_mod != b_mod {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
return strings.dice_coefficient(a_name, b_name)
|
return strings.dice_coefficient(a_name, b_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -982,7 +1004,9 @@ fn (table &Table) find_misspelled_fn(name string, p &Parser, min_match f32) stri
|
|||||||
mut closest := f32(0)
|
mut closest := f32(0)
|
||||||
mut closest_fn := ''
|
mut closest_fn := ''
|
||||||
for _, f in table.fns {
|
for _, f in table.fns {
|
||||||
if f.name.contains('__') && !p.is_mod_in_scope(f.mod) { continue }
|
if f.name.contains('__') && !p.is_mod_in_scope(f.mod) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
c := typo_compare_name_mod(name, f.name, f.mod)
|
c := typo_compare_name_mod(name, f.name, f.mod)
|
||||||
if c > closest {
|
if c > closest {
|
||||||
closest = c
|
closest = c
|
||||||
@ -1012,7 +1036,9 @@ fn (table &Table) find_misspelled_const(name string, p &Parser, min_match f32) s
|
|||||||
mut closest := f32(0)
|
mut closest := f32(0)
|
||||||
mut closest_const := ''
|
mut closest_const := ''
|
||||||
for cnst in table.consts {
|
for cnst in table.consts {
|
||||||
if cnst.name.contains('__') && !p.is_mod_in_scope(cnst.mod) { continue }
|
if cnst.name.contains('__') && !p.is_mod_in_scope(cnst.mod) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
c := typo_compare_name_mod(name, cnst.name, cnst.mod)
|
c := typo_compare_name_mod(name, cnst.name, cnst.mod)
|
||||||
if c > closest {
|
if c > closest {
|
||||||
closest = c
|
closest = c
|
||||||
@ -1023,12 +1049,14 @@ fn (table &Table) find_misspelled_const(name string, p &Parser, min_match f32) s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find type with closest name to `name`
|
// find type with closest name to `name`
|
||||||
fn (table &Table) find_misspelled_type(name string, p &Parser, min_match f32) (string, string) {
|
fn (table &Table) find_misspelled_type(name string, p &Parser, min_match f32) (string,string) {
|
||||||
mut closest := f32(0)
|
mut closest := f32(0)
|
||||||
mut closest_type := ''
|
mut closest_type := ''
|
||||||
mut type_cat := ''
|
mut type_cat := ''
|
||||||
for _, typ in table.typesmap {
|
for _, typ in table.typesmap {
|
||||||
if typ.name.contains('__') && !p.is_mod_in_scope(typ.mod) { continue }
|
if typ.name.contains('__') && !p.is_mod_in_scope(typ.mod) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
c := typo_compare_name_mod(name, typ.name, typ.mod)
|
c := typo_compare_name_mod(name, typ.name, typ.mod)
|
||||||
if c > closest {
|
if c > closest {
|
||||||
closest = c
|
closest = c
|
||||||
@ -1037,25 +1065,48 @@ fn (table &Table) find_misspelled_type(name string, p &Parser, min_match f32) (s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if closest >= min_match {
|
if closest >= min_match {
|
||||||
return closest_type, type_cat
|
return closest_type,type_cat
|
||||||
}
|
}
|
||||||
return '', ''
|
return '',''
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_cat_str(tc TypeCategory) string {
|
fn type_cat_str(tc TypeCategory) string {
|
||||||
tc_str := match tc {
|
tc_str := match tc {
|
||||||
.builtin { 'builtin' }
|
.builtin{
|
||||||
.struct_ { 'struct' }
|
'builtin'
|
||||||
.func { 'function' }
|
}
|
||||||
.interface_ { 'interface' }
|
.struct_{
|
||||||
.enum_ { 'enum' }
|
'struct'
|
||||||
.union_ { 'union' }
|
}
|
||||||
.c_struct { 'C struct' }
|
.func{
|
||||||
.c_typedef { 'C typedef' }
|
'function'
|
||||||
.objc_interface { 'obj C interface' }
|
}
|
||||||
.array { 'array' }
|
.interface_{
|
||||||
.alias { 'type alias' }
|
'interface'
|
||||||
else { 'unknown' }
|
}
|
||||||
}
|
.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
|
return tc_str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,11 +250,11 @@ fn (p &Parser) gen_fmt() {
|
|||||||
if s == '' {
|
if s == '' {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !p.file_path.contains('fn.v') {return}
|
if !p.file_path.contains('table.v') {return}
|
||||||
path := os.tmpdir() + '/' + p.file_name
|
path := os.tmpdir() + '/' + p.file_name
|
||||||
println('generating ${path}')
|
println('generating ${path}')
|
||||||
mut out := os.create(path) or {
|
mut out := os.create(path) or {
|
||||||
verror('failed to create fmt.v')
|
verror('failed to create os_nix.v')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
println('replacing ${p.file_path}...\n')
|
println('replacing ${p.file_path}...\n')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user