mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: import individual symbols feature (#5872)
This commit is contained in:
parent
b3011b4f19
commit
1114fd28d0
53
doc/docs.md
53
doc/docs.md
@ -29,7 +29,7 @@ you can do in V.
|
||||
* [Numbers](#numbers)
|
||||
* [Arrays](#arrays)
|
||||
* [Maps](#maps)
|
||||
* [Imports](#imports)
|
||||
* [Module Imports](#module-imports)
|
||||
* [Statements & Expressions](#statements--expressions)
|
||||
* [If](#if)
|
||||
* [In Operator](#in-operator)
|
||||
@ -547,7 +547,13 @@ numbers := {
|
||||
}
|
||||
```
|
||||
|
||||
## Imports
|
||||
## Module Imports
|
||||
|
||||
For information about creating a module, see [Modules](#modules)
|
||||
|
||||
### Importing a Module
|
||||
|
||||
Modules can be imported using keyword `import`.
|
||||
|
||||
```v
|
||||
import os
|
||||
@ -558,7 +564,48 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Modules can be imported using keyword `import`. When using types, functions, and constants from other modules, the full path must be specified. In the example above, `name := input()` wouldn't work. That means that it's always clear from which module a function is called.
|
||||
When using constants from other modules, the module name must be prefixed. However,
|
||||
you can import functions and types from other modules directly:
|
||||
|
||||
```v
|
||||
import os { input }
|
||||
import crypto.sha256 { sum }
|
||||
import time { Time }
|
||||
```
|
||||
|
||||
### Module Import Aliasing
|
||||
|
||||
Any imported module name can be aliased using the `as` keyword:
|
||||
|
||||
NOTE: this example will not compile unless you have created `mymod/sha256.v`
|
||||
```v
|
||||
import crypto.sha256
|
||||
import mymod.sha256 as mysha256
|
||||
|
||||
fn main() {
|
||||
v_hash := sha256.sum('hi'.bytes()).hex()
|
||||
my_hash := mysha256.sum('hi'.bytes()).hex()
|
||||
assert my_hash == v_hash
|
||||
}
|
||||
```
|
||||
|
||||
You cannot alias an imported function or type.
|
||||
However, you _can_ redeclare a type.
|
||||
|
||||
```v
|
||||
import time
|
||||
|
||||
type MyTime time.Time
|
||||
|
||||
fn main() {
|
||||
my_time := MyTime{
|
||||
year: 2020,
|
||||
month: 12,
|
||||
day: 25
|
||||
}
|
||||
println(my_time.unix_time())
|
||||
}
|
||||
```
|
||||
|
||||
## Statements & Expressions
|
||||
|
||||
|
@ -211,6 +211,20 @@ pub:
|
||||
pos token.Position
|
||||
mod string
|
||||
alias string
|
||||
pub mut:
|
||||
syms []ImportSymbol
|
||||
}
|
||||
|
||||
pub enum ImportSymbolKind {
|
||||
fn_
|
||||
type_
|
||||
}
|
||||
|
||||
pub struct ImportSymbol {
|
||||
pub:
|
||||
pos token.Position
|
||||
name string
|
||||
kind ImportSymbolKind
|
||||
}
|
||||
|
||||
pub struct AnonFn {
|
||||
|
@ -382,6 +382,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
||||
if type_sym.kind == .alias {
|
||||
info_t := type_sym.info as table.Alias
|
||||
sym := c.table.get_type_symbol(info_t.parent_type)
|
||||
if sym.kind == .placeholder { // pending import symbol did not resolve
|
||||
c.error('unknown struct: $type_sym.name', struct_init.pos)
|
||||
return table.void_type
|
||||
}
|
||||
if sym.kind != .struct_ {
|
||||
c.error('alias type name: $sym.name is not struct type', struct_init.pos)
|
||||
}
|
||||
@ -2055,7 +2059,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||
ast.GotoLabel {}
|
||||
ast.GotoStmt {}
|
||||
ast.HashStmt {}
|
||||
ast.Import {}
|
||||
ast.Import {
|
||||
c.import_stmt(node)
|
||||
}
|
||||
ast.InterfaceDecl {
|
||||
c.interface_decl(node)
|
||||
}
|
||||
@ -2087,6 +2093,26 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) import_stmt(imp ast.Import) {
|
||||
for sym in imp.syms {
|
||||
name := '$imp.mod\.$sym.name'
|
||||
if sym.kind == .fn_ {
|
||||
c.table.find_fn(name) or {
|
||||
c.error('module `$imp.mod` has no public fn named `$sym.name\()`', sym.pos)
|
||||
}
|
||||
}
|
||||
if sym.kind == .type_ {
|
||||
if type_sym := c.table.find_type(name) {
|
||||
if type_sym.kind == .placeholder {
|
||||
c.error('module `$imp.mod` has no public type `$sym.name\{}`', sym.pos)
|
||||
}
|
||||
} else {
|
||||
c.error('module `$imp.mod` has no public type `$sym.name\{}`', sym.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
||||
mut unreachable := token.Position{
|
||||
line_nr: -1
|
||||
@ -2919,7 +2945,6 @@ pub fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) table.Type {
|
||||
typ_sym := c.table.get_type_symbol(typ)
|
||||
// if !typ.is_number() {
|
||||
if !typ_sym.is_number() {
|
||||
println(typ_sym.kind.str())
|
||||
c.error('invalid operation: $node.op.str() (non-numeric type `$typ_sym.name`)',
|
||||
node.pos)
|
||||
} else {
|
||||
@ -3232,7 +3257,6 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||
mut idx := 0
|
||||
for i, m in sym.methods {
|
||||
if m.name == node.name {
|
||||
println('got it')
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
vlib/v/checker/tests/import_not_same_line_err.v:2:2: error: `import` and `module` must be at same line
|
||||
vlib/v/checker/tests/import_not_same_line_err.v:2:2: error: `import` statements must be a single line
|
||||
1 | import
|
||||
2 | time
|
||||
| ~~~~
|
||||
|
3
vlib/v/checker/tests/import_symbol_empty.out
Normal file
3
vlib/v/checker/tests/import_symbol_empty.out
Normal file
@ -0,0 +1,3 @@
|
||||
vlib/v/checker/tests/import_symbol_empty.v:1:12: error: empty `os` import set, remove `{}`
|
||||
1 | import os {}
|
||||
| ^
|
1
vlib/v/checker/tests/import_symbol_empty.vv
Normal file
1
vlib/v/checker/tests/import_symbol_empty.vv
Normal file
@ -0,0 +1 @@
|
||||
import os {}
|
11
vlib/v/checker/tests/import_symbol_fn_err.out
Normal file
11
vlib/v/checker/tests/import_symbol_fn_err.out
Normal file
@ -0,0 +1,11 @@
|
||||
vlib/v/checker/tests/import_symbol_fn_err.v:1:17: error: module `crypto` has no public fn named `userper()`
|
||||
1 | import crypto { userper }
|
||||
| ~~~~~~~
|
||||
2 | fn main() {
|
||||
3 | usurper()
|
||||
vlib/v/checker/tests/import_symbol_fn_err.v:3:3: error: unknown function: usurper
|
||||
1 | import crypto { userper }
|
||||
2 | fn main() {
|
||||
3 | usurper()
|
||||
| ~~~~~~~~~
|
||||
4 | }
|
4
vlib/v/checker/tests/import_symbol_fn_err.vv
Normal file
4
vlib/v/checker/tests/import_symbol_fn_err.vv
Normal file
@ -0,0 +1,4 @@
|
||||
import crypto { userper }
|
||||
fn main() {
|
||||
usurper()
|
||||
}
|
3
vlib/v/checker/tests/import_symbol_invalid.out
Normal file
3
vlib/v/checker/tests/import_symbol_invalid.out
Normal file
@ -0,0 +1,3 @@
|
||||
vlib/v/checker/tests/import_symbol_invalid.v:1:17: error: import syntax error, please specify a valid fn or type name
|
||||
1 | import crypto { *_v }
|
||||
| ^
|
1
vlib/v/checker/tests/import_symbol_invalid.vv
Normal file
1
vlib/v/checker/tests/import_symbol_invalid.vv
Normal file
@ -0,0 +1 @@
|
||||
import crypto { *_v }
|
11
vlib/v/checker/tests/import_symbol_type_err.out
Normal file
11
vlib/v/checker/tests/import_symbol_type_err.out
Normal file
@ -0,0 +1,11 @@
|
||||
vlib/v/checker/tests/import_symbol_type_err.v:1:17: error: module `crypto` has no public type `Coin{}`
|
||||
1 | import crypto { Coin }
|
||||
| ~~~~
|
||||
2 | fn main() {
|
||||
3 | println(Coin{})
|
||||
vlib/v/checker/tests/import_symbol_type_err.v:3:11: error: unknown struct: Coin
|
||||
1 | import crypto { Coin }
|
||||
2 | fn main() {
|
||||
3 | println(Coin{})
|
||||
| ~~~~~~
|
||||
4 | }
|
4
vlib/v/checker/tests/import_symbol_type_err.vv
Normal file
4
vlib/v/checker/tests/import_symbol_type_err.vv
Normal file
@ -0,0 +1,4 @@
|
||||
import crypto { Coin }
|
||||
fn main() {
|
||||
println(Coin{})
|
||||
}
|
3
vlib/v/checker/tests/import_symbol_unclosed.out
Normal file
3
vlib/v/checker/tests/import_symbol_unclosed.out
Normal file
@ -0,0 +1,3 @@
|
||||
vlib/v/checker/tests/import_symbol_unclosed.v:1:28: error: import syntax error, no closing `}`
|
||||
1 | import crypto.sha256 { sum ]
|
||||
| ^
|
1
vlib/v/checker/tests/import_symbol_unclosed.vv
Normal file
1
vlib/v/checker/tests/import_symbol_unclosed.vv
Normal file
@ -0,0 +1 @@
|
||||
import crypto.sha256 { sum ]
|
@ -1,4 +1,4 @@
|
||||
vlib/v/checker/tests/import_syntax_err.v:1:12: error: module syntax error, please use `x.y.z`
|
||||
vlib/v/checker/tests/import_syntax_err.v:1:12: error: cannot import multiple modules at a time
|
||||
1 | import time, os
|
||||
| ^
|
||||
2 | fn main() {
|
||||
|
@ -77,6 +77,10 @@ pub fn fmt(file ast.File, table &table.Table, is_debug bool) string {
|
||||
pub fn (mut f Fmt) process_file_imports(file &ast.File) {
|
||||
for imp in file.imports {
|
||||
f.mod2alias[imp.mod.all_after_last('.')] = imp.alias
|
||||
for sym in imp.syms {
|
||||
f.mod2alias['$imp.mod\.$sym.name'] = sym.name
|
||||
f.mod2alias[sym.name] = sym.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +232,10 @@ pub fn (mut f Fmt) imports(imports []ast.Import) {
|
||||
|
||||
pub fn (f Fmt) imp_stmt_str(imp ast.Import) string {
|
||||
is_diff := imp.alias != imp.mod && !imp.mod.ends_with('.' + imp.alias)
|
||||
imp_alias_suffix := if is_diff { ' as $imp.alias' } else { '' }
|
||||
mut imp_alias_suffix := if is_diff { ' as $imp.alias' } else { '' }
|
||||
if imp.syms.len > 0 {
|
||||
imp_alias_suffix += ' { ' + imp.syms.map(it.name).join(', ') + ' }'
|
||||
}
|
||||
return '$imp.mod$imp_alias_suffix'
|
||||
}
|
||||
|
||||
@ -1338,8 +1345,11 @@ pub fn (mut f Fmt) call_expr(node ast.CallExpr) {
|
||||
f.or_expr(node.or_block)
|
||||
} else {
|
||||
f.write_language_prefix(node.language)
|
||||
name := f.short_module(node.name)
|
||||
mut name := f.short_module(node.name)
|
||||
f.mark_module_as_used(name)
|
||||
if node.name in f.mod2alias {
|
||||
name = f.mod2alias[node.name]
|
||||
}
|
||||
f.write('$name')
|
||||
if node.generic_type != 0 && node.generic_type != table.void_type {
|
||||
f.write('<')
|
||||
|
@ -10,7 +10,7 @@ import v.util
|
||||
|
||||
pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExpr {
|
||||
first_pos := p.tok.position()
|
||||
fn_name := if language == .c {
|
||||
mut fn_name := if language == .c {
|
||||
'C.$p.check_name()'
|
||||
} else if language == .js {
|
||||
'JS.$p.check_js_name()'
|
||||
@ -81,10 +81,17 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||
p.next()
|
||||
or_kind = .propagate
|
||||
}
|
||||
mut fn_mod := p.mod
|
||||
if registered := p.table.find_fn(fn_name) {
|
||||
if registered.is_placeholder {
|
||||
fn_mod = registered.mod
|
||||
fn_name = registered.name
|
||||
}
|
||||
}
|
||||
node := ast.CallExpr{
|
||||
name: fn_name
|
||||
args: args
|
||||
mod: p.mod
|
||||
mod: fn_mod
|
||||
pos: pos
|
||||
language: language
|
||||
or_block: ast.OrExpr{
|
||||
|
@ -1331,7 +1331,7 @@ fn (mut p Parser) import_stmt() ast.Import {
|
||||
}
|
||||
mut mod_name := p.check_name()
|
||||
if import_pos.line_nr != pos.line_nr {
|
||||
p.error_with_pos('`import` and `module` must be at same line', pos)
|
||||
p.error_with_pos('`import` statements must be a single line', pos)
|
||||
}
|
||||
mut mod_alias := mod_name
|
||||
for p.tok.kind == .dot {
|
||||
@ -1351,25 +1351,90 @@ fn (mut p Parser) import_stmt() ast.Import {
|
||||
p.next()
|
||||
mod_alias = p.check_name()
|
||||
}
|
||||
node := ast.Import{
|
||||
pos: pos,
|
||||
mod: mod_name,
|
||||
alias: mod_alias,
|
||||
}
|
||||
if p.tok.kind == .lcbr { // import module { fn1, Type2 } syntax
|
||||
p.import_syms(node)
|
||||
p.register_used_import(mod_name) // no `unused import` msg for parent
|
||||
}
|
||||
pos_t := p.tok.position()
|
||||
if import_pos.line_nr == pos_t.line_nr {
|
||||
if p.tok.kind != .name {
|
||||
p.error_with_pos('module syntax error, please use `x.y.z`', pos_t)
|
||||
} else {
|
||||
if p.tok.kind != .lcbr {
|
||||
p.error_with_pos('cannot import multiple modules at a time', pos_t)
|
||||
}
|
||||
}
|
||||
p.imports[mod_alias] = mod_name
|
||||
p.table.imports << mod_name
|
||||
node := ast.Import{
|
||||
mod: mod_name
|
||||
alias: mod_alias
|
||||
pos: pos
|
||||
}
|
||||
p.ast_imports << node
|
||||
return node
|
||||
}
|
||||
|
||||
// import_syms parses the inner part of `import module { submod1, submod2 }`
|
||||
fn (mut p Parser) import_syms(mut parent ast.Import) {
|
||||
p.next()
|
||||
pos_t := p.tok.position()
|
||||
if p.tok.kind == .rcbr { // closed too early
|
||||
p.error_with_pos('empty `$parent.mod` import set, remove `{}`', pos_t)
|
||||
}
|
||||
if p.tok.kind != .name { // not a valid inner name
|
||||
p.error_with_pos('import syntax error, please specify a valid fn or type name', pos_t)
|
||||
}
|
||||
for p.tok.kind == .name {
|
||||
pos := p.tok.position()
|
||||
alias := p.check_name()
|
||||
name := '$parent.mod\.$alias'
|
||||
if alias[0].is_capital() {
|
||||
idx := p.table.add_placeholder_type(name)
|
||||
typ := table.new_type(idx)
|
||||
p.table.register_type_symbol({
|
||||
kind: .alias
|
||||
name: p.prepend_mod(alias)
|
||||
parent_idx: idx
|
||||
mod: p.mod
|
||||
info: table.Alias{
|
||||
parent_type: typ
|
||||
language: table.Language.v
|
||||
}
|
||||
is_public: false
|
||||
})
|
||||
// so we can work with the fully declared type in fmt+checker
|
||||
parent.syms << ast.ImportSymbol{
|
||||
pos: pos
|
||||
name: alias
|
||||
kind: .type_
|
||||
}
|
||||
} else {
|
||||
if !p.table.known_fn(name) {
|
||||
p.table.fns[alias] = table.Fn{
|
||||
is_placeholder: true
|
||||
mod: parent.mod
|
||||
name: name
|
||||
}
|
||||
}
|
||||
// so we can work with this in fmt+checker
|
||||
parent.syms << ast.ImportSymbol{
|
||||
pos: pos
|
||||
name: alias
|
||||
kind: .fn_
|
||||
}
|
||||
}
|
||||
if p.tok.kind == .comma { // go again if more than one
|
||||
p.next()
|
||||
continue
|
||||
}
|
||||
if p.tok.kind == .rcbr { // finish if closing `}` is seen
|
||||
break
|
||||
}
|
||||
}
|
||||
if p.tok.kind != .rcbr {
|
||||
p.error_with_pos('import syntax error, no closing `}`', p.tok.position())
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
|
||||
fn (mut p Parser) const_decl() ast.ConstDecl {
|
||||
p.top_level_statement_start()
|
||||
start_pos := p.tok.position()
|
||||
|
@ -22,19 +22,20 @@ pub mut:
|
||||
|
||||
pub struct Fn {
|
||||
pub:
|
||||
args []Arg
|
||||
return_type Type
|
||||
is_variadic bool
|
||||
language Language
|
||||
is_generic bool
|
||||
is_pub bool
|
||||
is_deprecated bool
|
||||
is_unsafe bool
|
||||
mod string
|
||||
ctdefine string // compile time define. myflag, when [if myflag] tag
|
||||
attrs []string
|
||||
args []Arg
|
||||
return_type Type
|
||||
is_variadic bool
|
||||
language Language
|
||||
is_generic bool
|
||||
is_pub bool
|
||||
is_deprecated bool
|
||||
is_unsafe bool
|
||||
is_placeholder bool
|
||||
mod string
|
||||
ctdefine string // compile time define. myflag, when [if myflag] tag
|
||||
attrs []string
|
||||
pub mut:
|
||||
name string
|
||||
name string
|
||||
}
|
||||
|
||||
pub struct Arg {
|
||||
|
@ -1,8 +1,10 @@
|
||||
import os
|
||||
import time as t
|
||||
import crypto.sha256
|
||||
import math
|
||||
import term { white }
|
||||
import crypto.md5 { sum }
|
||||
import log as l
|
||||
import time as t { now, utc, Time }
|
||||
import math
|
||||
import crypto.sha512
|
||||
|
||||
struct TestAliasInStruct {
|
||||
@ -11,8 +13,15 @@ struct TestAliasInStruct {
|
||||
|
||||
fn test_import() {
|
||||
info := l.Level.info
|
||||
assert os.o_rdonly == os.o_rdonly && t.month_days[0] == t.month_days[0] && sha256.size ==
|
||||
sha256.size && math.pi == math.pi && info == .info && sha512.size == sha512.size
|
||||
assert info == .info
|
||||
assert term.white('INFO') == white('INFO')
|
||||
assert os.o_rdonly == os.o_rdonly
|
||||
assert t.month_days[0] == t.month_days[0]
|
||||
assert sha256.size == sha256.size
|
||||
assert math.pi == math.pi
|
||||
assert sha512.size == sha512.size
|
||||
assert md5.sum('module'.bytes()).hex() == sum('module'.bytes()).hex()
|
||||
assert t.utc().unix_time() == utc().unix_time()
|
||||
}
|
||||
|
||||
fn test_alias_in_struct_field() {
|
||||
|
Loading…
Reference in New Issue
Block a user