Merge branch 'master' of https://github.com/vlang/v into add-rsa

pull/13324/head
Louis Schmieder 2022-02-02 10:07:31 +00:00 committed by GitHub
commit 8f9e37a58f
27 changed files with 519 additions and 125 deletions

View File

@ -55,6 +55,9 @@ jobs:
cd '${{env.MY_V_PATH}}' cd '${{env.MY_V_PATH}}'
ls -la ls -la
make make
## prebuild cmd/tools/builders/js_builder, to minimise the
## chances of a sporadic "Killed" when running the tests later
./v -b js run examples/hello_world.v
- name: v doctor - name: v doctor
run: | run: |
cd '${{env.MY_V_PATH}}' cd '${{env.MY_V_PATH}}'

View File

@ -21,6 +21,7 @@ const (
hide_warnings = '-hide-warnings' in os.args || '-w' in os.args hide_warnings = '-hide-warnings' in os.args || '-w' in os.args
show_progress = os.getenv('GITHUB_JOB') == '' && '-silent' !in os.args show_progress = os.getenv('GITHUB_JOB') == '' && '-silent' !in os.args
non_option_args = cmdline.only_non_options(os.args[2..]) non_option_args = cmdline.only_non_options(os.args[2..])
is_verbose = os.getenv('VERBOSE') != ''
) )
struct CheckResult { struct CheckResult {
@ -75,7 +76,7 @@ fn main() {
res += mdfile.check() res += mdfile.check()
} }
if res.errors == 0 && show_progress { if res.errors == 0 && show_progress {
term.clear_previous_line() clear_previous_line()
} }
if res.warnings > 0 || res.errors > 0 || res.oks > 0 { if res.warnings > 0 || res.errors > 0 || res.oks > 0 {
println('\nWarnings: $res.warnings | Errors: $res.errors | OKs: $res.oks') println('\nWarnings: $res.warnings | Errors: $res.errors | OKs: $res.oks')
@ -131,9 +132,7 @@ fn eline(file_path string, lnumber int, column int, message string) string {
return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(rtext(' error: $message')) return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(rtext(' error: $message'))
} }
const ( const default_command = 'compile'
default_command = 'compile'
)
struct VCodeExample { struct VCodeExample {
mut: mut:
@ -160,7 +159,7 @@ mut:
fn (mut f MDFile) progress(message string) { fn (mut f MDFile) progress(message string) {
if show_progress { if show_progress {
term.clear_previous_line() clear_previous_line()
println('File: ${f.path:-30s}, Lines: ${f.lines.len:5}, $message') println('File: ${f.path:-30s}, Lines: ${f.lines.len:5}, $message')
} }
} }
@ -394,6 +393,7 @@ fn (mut f MDFile) debug() {
} }
fn cmdexecute(cmd string) int { fn cmdexecute(cmd string) int {
verbose_println(cmd)
res := os.execute(cmd) res := os.execute(cmd)
if res.exit_code < 0 { if res.exit_code < 0 {
return 1 return 1
@ -405,6 +405,7 @@ fn cmdexecute(cmd string) int {
} }
fn silent_cmdexecute(cmd string) int { fn silent_cmdexecute(cmd string) int {
verbose_println(cmd)
res := os.execute(cmd) res := os.execute(cmd)
return res.exit_code return res.exit_code
} }
@ -426,6 +427,7 @@ fn (mut f MDFile) check_examples() CheckResult {
} }
fname := os.base(f.path).replace('.md', '_md') fname := os.base(f.path).replace('.md', '_md')
uid := rand.ulid() uid := rand.ulid()
cfile := os.join_path(os.temp_dir(), '${uid}.c')
vfile := os.join_path(os.temp_dir(), 'check_${fname}_example_${e.sline}__${e.eline}__${uid}.v') vfile := os.join_path(os.temp_dir(), 'check_${fname}_example_${e.sline}__${e.eline}__${uid}.v')
mut should_cleanup_vfile := true mut should_cleanup_vfile := true
// eprintln('>>> checking example $vfile ...') // eprintln('>>> checking example $vfile ...')
@ -438,8 +440,7 @@ fn (mut f MDFile) check_examples() CheckResult {
fmt_res := if nofmt { 0 } else { get_fmt_exit_code(vfile, vexe) } fmt_res := if nofmt { 0 } else { get_fmt_exit_code(vfile, vexe) }
match command { match command {
'compile' { 'compile' {
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -o x.c ${os.quoted_path(vfile)}') res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors ${os.quoted_path(vfile)}')
os.rm('x.c') or {}
if res != 0 || fmt_res != 0 { if res != 0 || fmt_res != 0 {
if res != 0 { if res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example failed to compile')) eprintln(eline(f.path, e.sline, 0, 'example failed to compile'))
@ -454,9 +455,26 @@ fn (mut f MDFile) check_examples() CheckResult {
} }
oks++ oks++
} }
'cgen' {
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -o ${os.quoted_path(cfile)} ${os.quoted_path(vfile)}')
os.rm(cfile) or {}
if res != 0 || fmt_res != 0 {
if res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example failed to generate C code'))
}
if fmt_res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example is not formatted'))
}
eprintln(vcontent)
should_cleanup_vfile = false
errors++
continue
}
oks++
}
'globals' { 'globals' {
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -enable-globals -o x.c ${os.quoted_path(vfile)}') res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -enable-globals -o ${os.quoted_path(cfile)} ${os.quoted_path(vfile)}')
os.rm('x.c') or {} os.rm(cfile) or {}
if res != 0 || fmt_res != 0 { if res != 0 || fmt_res != 0 {
if res != 0 { if res != 0 {
eprintln(eline(f.path, e.sline, 0, '`example failed to compile with -enable-globals')) eprintln(eline(f.path, e.sline, 0, '`example failed to compile with -enable-globals'))
@ -472,7 +490,8 @@ fn (mut f MDFile) check_examples() CheckResult {
oks++ oks++
} }
'live' { 'live' {
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -live -o x.c ${os.quoted_path(vfile)}') res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -live -o ${os.quoted_path(cfile)} ${os.quoted_path(vfile)}')
os.rm(cfile) or {}
if res != 0 || fmt_res != 0 { if res != 0 || fmt_res != 0 {
if res != 0 { if res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example failed to compile with -live')) eprintln(eline(f.path, e.sline, 0, 'example failed to compile with -live'))
@ -488,8 +507,8 @@ fn (mut f MDFile) check_examples() CheckResult {
oks++ oks++
} }
'failcompile' { 'failcompile' {
res := silent_cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -o x.c ${os.quoted_path(vfile)}') res := silent_cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -o ${os.quoted_path(cfile)} ${os.quoted_path(vfile)}')
os.rm('x.c') or {} os.rm(cfile) or {}
if res == 0 || fmt_res != 0 { if res == 0 || fmt_res != 0 {
if res == 0 { if res == 0 {
eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled')) eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled'))
@ -533,7 +552,7 @@ fn (mut f MDFile) check_examples() CheckResult {
} }
'nofmt' {} 'nofmt' {}
else { else {
eprintln(eline(f.path, e.sline, 0, 'unrecognized command: "$command", use one of: wip/ignore/compile/failcompile/oksyntax/badsyntax')) eprintln(eline(f.path, e.sline, 0, 'unrecognized command: "$command", use one of: wip/ignore/compile/cgen/failcompile/oksyntax/badsyntax/nofmt'))
should_cleanup_vfile = false should_cleanup_vfile = false
errors++ errors++
} }
@ -548,3 +567,16 @@ fn (mut f MDFile) check_examples() CheckResult {
oks: oks oks: oks
} }
} }
fn verbose_println(message string) {
if is_verbose {
println(message)
}
}
fn clear_previous_line() {
if is_verbose {
return
}
term.clear_previous_line()
}

View File

@ -259,7 +259,15 @@ fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path
file_bak := '${file}.bak' file_bak := '${file}.bak'
os.cp(file, file_bak) or {} os.cp(file, file_bak) or {}
} }
mut perms_to_restore := u32(0)
$if !windows {
fm := os.inode(file)
perms_to_restore = fm.bitmask()
}
os.mv_by_cp(formatted_file_path, file) or { panic(err) } os.mv_by_cp(formatted_file_path, file) or { panic(err) }
$if !windows {
os.chmod(file, int(perms_to_restore)) or { panic(err) }
}
eprintln('Reformatted file: $file') eprintln('Reformatted file: $file')
} else { } else {
eprintln('Already formatted file: $file') eprintln('Already formatted file: $file')

View File

@ -13,6 +13,7 @@ Flags:
NB: There are several special keywords, which you can put after the code fences for v. NB: There are several special keywords, which you can put after the code fences for v.
These are: These are:
compile - Default, can be omitted. The example will be compiled and formatting is verified. compile - Default, can be omitted. The example will be compiled and formatting is verified.
cgen - The example produces C code, which may not be compilable (when external libs are not installed). Formatting is verified.
live - Compile hot reload examples with the ´-live´ flag set and verify formatting. live - Compile hot reload examples with the ´-live´ flag set and verify formatting.
ignore - Ignore the example, useful for examples that just use the syntax highlighting ignore - Ignore the example, useful for examples that just use the syntax highlighting
failcompile - Known failing compilation. Useful for examples demonstrating compiler errors. failcompile - Known failing compilation. Useful for examples demonstrating compiler errors.

View File

@ -163,7 +163,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
<!-- <!--
NB: there are several special keywords, which you can put after the code fences for v: NB: there are several special keywords, which you can put after the code fences for v:
compile, live, ignore, failcompile, oksyntax, badsyntax, wip, nofmt compile, cgen, live, ignore, failcompile, oksyntax, badsyntax, wip, nofmt
For more details, do: `v check-md` For more details, do: `v check-md`
--> -->
@ -1393,7 +1393,7 @@ println(s)
You can check the current type of a sum type using `is` and its negated form `!is`. You can check the current type of a sum type using `is` and its negated form `!is`.
You can do it either in an `if`: You can do it either in an `if`:
```v ```v cgen
struct Abc { struct Abc {
val string val string
} }
@ -2789,38 +2789,52 @@ For more information, see [Dynamic casts](#dynamic-casts).
#### Interface method definitions #### Interface method definitions
Also unlike Go, an interface may implement a method. Also unlike Go, an interface can have it's own methods, similar to how
These methods are not implemented by structs which implement that interface. structs can have their methods. These 'interface methods' do not have
to be implemented, by structs which implement that interface.
They are just a convenient way to write `i.some_function()` instead of
`some_function(i)`, similar to how struct methods can be looked at, as
a convenience for writing `s.xyz()` instead of `xyz(s)`.
When a struct is wrapped in an interface that has implemented a method N.B. This feature is NOT a "default implementation" like in C#.
with the same name as one implemented by this struct, only the method
implemented on the interface is called. For example, if a struct `cat` is wrapped in an interface `a`, that has
implemented a method with the same name `speak`, as a method implemented by
the struct, and you do `a.speak()`, *only* the interface method is called:
```v ```v
struct Cat {}
fn (c Cat) speak() string {
return 'meow!'
}
interface Adoptable {} interface Adoptable {}
fn (a Adoptable) speak() string { fn (a Adoptable) speak() string {
return 'adopt me!' return 'adopt me!'
} }
fn new_adoptable() Adoptable { struct Cat {}
return Cat{}
fn (c Cat) speak() string {
return 'meow!'
} }
struct Dog {}
fn main() { fn main() {
cat := Cat{} cat := Cat{}
assert cat.speak() == 'meow!' assert dump(cat.speak()) == 'meow!'
a := new_adoptable() //
assert a.speak() == 'adopt me!' a := Adoptable(cat)
assert dump(a.speak()) == 'adopt me!' // call Adoptable's `speak`
if a is Cat { if a is Cat {
println(a.speak()) // meow! // Inside this `if` however, V knows that `a` is not just any
// kind of Adoptable, but actually a Cat, so it will use the
// Cat `speak`, NOT the Adoptable `speak`:
dump(a.speak()) // meow!
} }
//
b := Adoptable(Dog{})
assert dump(b.speak()) == 'adopt me!' // call Adoptable's `speak`
// if b is Dog {
// dump(b.speak()) // error: unknown method or field: Dog.speak
// }
} }
``` ```
@ -5102,6 +5116,7 @@ For all supported options check the latest help:
#### `$if` condition #### `$if` condition
```v ```v
fn main() {
// Support for multiple conditions in one branch // Support for multiple conditions in one branch
$if ios || android { $if ios || android {
println('Running on a mobile device!') println('Running on a mobile device!')
@ -5137,6 +5152,7 @@ $if prod {
$if option ? { $if option ? {
println('custom option') println('custom option')
} }
}
``` ```
If you want an `if` to be evaluated at compile time it must be prefixed with a `$` sign. If you want an `if` to be evaluated at compile time it must be prefixed with a `$` sign.

View File

@ -204,7 +204,9 @@ pub fn (mut a array) insert(i int, val voidptr) {
panic('array.insert: index out of range (i == $i, a.len == $a.len)') panic('array.insert: index out of range (i == $i, a.len == $a.len)')
} }
} }
if a.len >= a.cap {
a.ensure_cap(a.len + 1) a.ensure_cap(a.len + 1)
}
unsafe { unsafe {
vmemmove(a.get_unsafe(i + 1), a.get_unsafe(i), (a.len - i) * a.element_size) vmemmove(a.get_unsafe(i + 1), a.get_unsafe(i), (a.len - i) * a.element_size)
a.set_unsafe(i, val) a.set_unsafe(i, val)
@ -569,7 +571,9 @@ fn (mut a array) set(i int, val voidptr) {
} }
fn (mut a array) push(val voidptr) { fn (mut a array) push(val voidptr) {
if a.len >= a.cap {
a.ensure_cap(a.len + 1) a.ensure_cap(a.len + 1)
}
unsafe { vmemmove(&byte(a.data) + a.element_size * a.len, val, a.element_size) } unsafe { vmemmove(&byte(a.data) + a.element_size * a.len, val, a.element_size) }
a.len++ a.len++
} }
@ -578,16 +582,15 @@ fn (mut a array) push(val voidptr) {
// `val` is array.data and user facing usage is `a << [1,2,3]` // `val` is array.data and user facing usage is `a << [1,2,3]`
[unsafe] [unsafe]
pub fn (mut a3 array) push_many(val voidptr, size int) { pub fn (mut a3 array) push_many(val voidptr, size int) {
a3.ensure_cap(a3.len + size)
if a3.data == val && !isnil(a3.data) { if a3.data == val && !isnil(a3.data) {
// handle `arr << arr` // handle `arr << arr`
copy := a3.clone() copy := a3.clone()
a3.ensure_cap(a3.len + size)
unsafe { unsafe {
// vmemcpy(a.data, copy.data, copy.element_size * copy.len) // vmemcpy(a.data, copy.data, copy.element_size * copy.len)
vmemcpy(a3.get_unsafe(a3.len), copy.data, a3.element_size * size) vmemcpy(a3.get_unsafe(a3.len), copy.data, a3.element_size * size)
} }
} else { } else {
a3.ensure_cap(a3.len + size)
if !isnil(a3.data) && !isnil(val) { if !isnil(a3.data) && !isnil(val) {
unsafe { vmemcpy(a3.get_unsafe(a3.len), val, a3.element_size * size) } unsafe { vmemcpy(a3.get_unsafe(a3.len), val, a3.element_size * size) }
} }

View File

@ -1166,7 +1166,9 @@ fn test_array_int_pop() {
assert a.len == 3 assert a.len == 3
assert z == 4 assert z == 4
x1 := a.pop() x1 := a.pop()
println(x1)
x2 := a.pop() x2 := a.pop()
println(x2)
final := a.pop() final := a.pop()
assert final == 1 assert final == 1
} }

View File

@ -99,6 +99,7 @@ fn test_hex() {
b := 1234 b := 1234
assert b.hex() == '4d2' assert b.hex() == '4d2'
b1 := -1 b1 := -1
println(b1)
// assert b1.hex() == 'ffffffff' // assert b1.hex() == 'ffffffff'
// unsigned tests // unsigned tests
// assert u8(12).hex() == '0c' // assert u8(12).hex() == '0c'

View File

@ -372,6 +372,9 @@ fn test_map_assign() {
'r': u16(6) 'r': u16(6)
's': 5 's': 5
}} }}
println(a)
println(b)
println(c)
} }
fn test_postfix_op_directly() { fn test_postfix_op_directly() {

View File

@ -1,4 +1,4 @@
import strings // import strings
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019-2022 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
@ -636,6 +636,7 @@ fn test_for_loop_two() {
fn test_quote() { fn test_quote() {
a := `'` a := `'`
println(a)
println('testing double quotes') println('testing double quotes')
b := 'hi' b := 'hi'
assert b == 'hi' assert b == 'hi'
@ -727,6 +728,7 @@ fn test_raw() {
fn test_raw_with_quotes() { fn test_raw_with_quotes() {
raw := r"some'" + r'"thing' // " should be escaped in the generated C code raw := r"some'" + r'"thing' // " should be escaped in the generated C code
println(raw)
// assert raw[0] == `s` // assert raw[0] == `s`
// assert raw[5] == `"` // assert raw[5] == `"`
// assert raw[6] == `t` // assert raw[6] == `t`

View File

@ -7,7 +7,7 @@ user's keyboard/mouse input.
## Example: ## Example:
```v ```v cgen
module main module main
import gg import gg

View File

@ -20,6 +20,22 @@ pub:
execute bool execute bool
} }
// bitmask returns a 3 bit sequence in the order RWE where
// the bit is set to 1 if the value is true or 0 otherwise.
pub fn (p FilePermission) bitmask() u32 {
mut mask := u32(0)
if p.read {
mask |= 4
}
if p.write {
mask |= 2
}
if p.execute {
mask |= 1
}
return mask
}
struct FileMode { struct FileMode {
pub: pub:
typ FileType typ FileType
@ -28,6 +44,12 @@ pub:
others FilePermission others FilePermission
} }
// bitmask returns a 9 bit sequence in the order owner + group + others.
// This is a valid bitmask to use with `os.chmod`.
pub fn (m FileMode) bitmask() u32 {
return m.owner.bitmask() << 6 | m.group.bitmask() << 3 | m.others.bitmask()
}
// inode returns the mode of the file/inode containing inode type and permission information // inode returns the mode of the file/inode containing inode type and permission information
// it supports windows for regular files but it doesn't matter if you use owner, group or others when checking permissions on windows // it supports windows for regular files but it doesn't matter if you use owner, group or others when checking permissions on windows
pub fn inode(path string) FileMode { pub fn inode(path string) FileMode {

View File

@ -41,3 +41,16 @@ fn test_inode_file_owner_permission() {
assert mode.owner.write assert mode.owner.write
assert !mode.owner.execute assert !mode.owner.execute
} }
fn test_inode_file_permissions_bitmask() {
if user_os() == 'windows' {
println('> skipping ${@FN} on windows')
return
}
filename := './test3.txt'
mut file := open_file(filename, 'w', 0o641) or { return }
file.close()
mode := inode(filename)
rm(filename) or {}
assert mode.bitmask() == 0o641
}

View File

@ -2024,6 +2024,11 @@ pub fn (mut re RE) match_base(in_txt &byte, in_txt_len int) (int, int) {
re.prog[state.pc].group_rep++ // increase repetitions re.prog[state.pc].group_rep++ // increase repetitions
// println("GROUP $group_index END ${re.prog[state.pc].group_rep}") // println("GROUP $group_index END ${re.prog[state.pc].group_rep}")
if re.prog[state.pc].group_rep > in_txt_len - 1 {
m_state = .ist_quant_ng
continue
}
m_state = .ist_quant_pg m_state = .ist_quant_pg
continue continue
} }

View File

@ -160,6 +160,11 @@ match_test_suite = [
TestItem{"a", r"\S+",0,1}, TestItem{"a", r"\S+",0,1},
TestItem{"aaaa", r"\S+",0,4}, TestItem{"aaaa", r"\S+",0,4},
TestItem{"aaaa ", r"\S+",0,4}, TestItem{"aaaa ", r"\S+",0,4},
// multiple dot char
TestItem{"aba", r"a*(b*)*a",0,3},
TestItem{"/*x*/", r"/\**(.*)\**/",0,5},
TestItem{"/*x*/", r"/*(.*)*/",0,5},
] ]
) )
@ -591,6 +596,21 @@ fn test_regex_func(){
} }
} }
fn my_repl_1(re regex.RE, in_txt string, start int, end int) string {
s0 := re.get_group_by_id(in_txt,0)
println("[$start, $end] => ${s0}")
return "a" + s0.to_upper()
}
fn test_regex_func_replace1(){
txt := "abbabbbabbbbaabba"
query := r"a(b+)"
mut re := regex.regex_opt(query) or { panic(err) }
result := re.replace_by_fn(txt, my_repl_1)
assert result == "aBBaBBBaBBBBaaBBa"
}
fn my_repl(re regex.RE, in_txt string, start int, end int) string { fn my_repl(re regex.RE, in_txt string, start int, end int) string {
s0 := re.get_group_by_id(in_txt,0)[0..1] + "X" s0 := re.get_group_by_id(in_txt,0)[0..1] + "X"
s1 := re.get_group_by_id(in_txt,1)[0..1] + "X" s1 := re.get_group_by_id(in_txt,1)[0..1] + "X"
@ -598,7 +618,6 @@ fn my_repl(re regex.RE, in_txt string, start int, end int) string {
return "${s0}${s1}${s2}" return "${s0}${s1}${s2}"
} }
// test regex replace function // test regex replace function
fn test_regex_func_replace(){ fn test_regex_func_replace(){
filler := "E il primo dei tre regni dell'Oltretomba cristiano visitato da Dante nel corso del viaggio, con la guida di Virgilio." filler := "E il primo dei tre regni dell'Oltretomba cristiano visitato da Dante nel corso del viaggio, con la guida di Virgilio."

View File

@ -212,7 +212,7 @@ pub fn (mut re RE) find(in_txt string) (int, int) {
[direct_array_access] [direct_array_access]
pub fn (mut re RE) find_from(in_txt string, start int) (int, int) { pub fn (mut re RE) find_from(in_txt string, start int) (int, int) {
old_flag := re.flag old_flag := re.flag
re.flag |= f_src // enable search mode // re.flag |= f_src // enable search mode
mut i := start mut i := start
if i < 0 { if i < 0 {

View File

@ -11,7 +11,7 @@ Each `.h` file in the sokol source code is well-documented as can be seen here:
## Example from `@VROOTDIR/examples/sokol/sounds/simple_sin_tones.v`: ## Example from `@VROOTDIR/examples/sokol/sounds/simple_sin_tones.v`:
```v ```v cgen
import time import time
import math import math
import sokol.audio import sokol.audio

View File

@ -446,6 +446,18 @@ pub fn (t &Table) find_method_with_embeds(sym &TypeSymbol, method_name string) ?
} }
} }
pub fn (t &Table) get_embed_methods(sym &TypeSymbol) []Fn {
mut methods := []Fn{}
if sym.info is Struct {
for embed in sym.info.embeds {
embed_sym := t.sym(embed)
methods << embed_sym.methods
methods << t.get_embed_methods(embed_sym)
}
}
return methods
}
fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?StructField { fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?StructField {
if sym.kind != .aggregate { if sym.kind != .aggregate {
t.panic('Unexpected type symbol: $sym.kind') t.panic('Unexpected type symbol: $sym.kind')
@ -1428,8 +1440,12 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
if typ == 0 { if typ == 0 {
return none return none
} }
if typ.has_flag(.generic) {
return typ.derive_add_muls(generic_type).set_flag(.generic)
} else {
return typ.derive_add_muls(generic_type).clear_flag(.generic) return typ.derive_add_muls(generic_type).clear_flag(.generic)
} }
}
match mut sym.info { match mut sym.info {
Array { Array {
mut elem_type := sym.info.elem_type mut elem_type := sym.info.elem_type
@ -1443,32 +1459,48 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
} }
if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types) { if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types) {
idx := t.find_or_register_array_with_dims(typ, dims) idx := t.find_or_register_array_with_dims(typ, dims)
if typ.has_flag(.generic) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
} }
} }
}
ArrayFixed { ArrayFixed {
if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names,
concrete_types) concrete_types)
{ {
idx := t.find_or_register_array_fixed(typ, sym.info.size, None{}) idx := t.find_or_register_array_fixed(typ, sym.info.size, None{})
if typ.has_flag(.generic) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
} }
} }
}
Chan { Chan {
if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names,
concrete_types) concrete_types)
{ {
idx := t.find_or_register_chan(typ, typ.nr_muls() > 0) idx := t.find_or_register_chan(typ, typ.nr_muls() > 0)
if typ.has_flag(.generic) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
} }
} }
}
FnType { FnType {
mut func := sym.info.func mut func := sym.info.func
mut has_generic := false
if func.return_type.has_flag(.generic) { if func.return_type.has_flag(.generic) {
if typ := t.resolve_generic_to_concrete(func.return_type, generic_names, if typ := t.resolve_generic_to_concrete(func.return_type, generic_names,
concrete_types) concrete_types)
{ {
func.return_type = typ func.return_type = typ
if typ.has_flag(.generic) {
has_generic = true
}
} }
} }
func.params = func.params.clone() func.params = func.params.clone()
@ -1478,13 +1510,20 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
concrete_types) concrete_types)
{ {
param.typ = typ param.typ = typ
if typ.has_flag(.generic) {
has_generic = true
}
} }
} }
} }
func.name = '' func.name = ''
idx := t.find_or_register_fn_type('', func, true, false) idx := t.find_or_register_fn_type('', func, true, false)
if has_generic {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
} }
}
MultiReturn { MultiReturn {
mut types := []Type{} mut types := []Type{}
mut type_changed := false mut type_changed := false
@ -1498,9 +1537,13 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
} }
if type_changed { if type_changed {
idx := t.find_or_register_multi_return(types) idx := t.find_or_register_multi_return(types)
if types.any(it.has_flag(.generic)) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
} }
} }
}
Map { Map {
mut type_changed := false mut type_changed := false
mut unwrapped_key_type := sym.info.key_type mut unwrapped_key_type := sym.info.key_type
@ -1519,9 +1562,13 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
} }
if type_changed { if type_changed {
idx := t.find_or_register_map(unwrapped_key_type, unwrapped_value_type) idx := t.find_or_register_map(unwrapped_key_type, unwrapped_value_type)
if unwrapped_key_type.has_flag(.generic) || unwrapped_value_type.has_flag(.generic) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
} }
} }
}
Struct, Interface, SumType { Struct, Interface, SumType {
if sym.info.is_generic { if sym.info.is_generic {
mut nrt := '$sym.name<' mut nrt := '$sym.name<'

View File

@ -963,6 +963,13 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
} }
if func.generic_names.len > 0 { if func.generic_names.len > 0 {
if has_generic { if has_generic {
if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
node.concrete_types)
{
if typ.has_flag(.generic) {
node.return_type = typ
}
}
return node.return_type return node.return_type
} else if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names, } else if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
concrete_types) concrete_types)

View File

@ -2471,10 +2471,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
} }
// Generic dereferencing logic // Generic dereferencing logic
neither_void := ast.voidptr_type !in [got_type, expected_type] neither_void := ast.voidptr_type !in [got_type, expected_type]
to_shared := expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f) if expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f)
&& !expected_type.has_flag(.optional) && !expected_type.has_flag(.optional) {
// from_shared := got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f)
if to_shared {
shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*` shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*`
if got_type_raw.is_ptr() { if got_type_raw.is_ptr() {
g.error('cannot convert reference to `shared`', expr.pos()) g.error('cannot convert reference to `shared`', expr.pos())
@ -2492,6 +2490,13 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
g.is_shared = old_is_shared g.is_shared = old_is_shared
g.writeln('}, sizeof($shared_styp))') g.writeln('}, sizeof($shared_styp))')
return return
} else if got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) {
if expected_type.is_ptr() {
g.write('&')
}
g.expr(expr)
g.write('->val')
return
} }
if got_is_ptr && !expected_is_ptr && neither_void && exp_sym.kind != .placeholder if got_is_ptr && !expected_is_ptr && neither_void && exp_sym.kind != .placeholder
&& expr !is ast.InfixExpr { && expr !is ast.InfixExpr {
@ -3604,10 +3609,15 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
} }
// struct embedding // struct embedding
if sym.info in [ast.Struct, ast.Aggregate] { if sym.info in [ast.Struct, ast.Aggregate] {
for embed in node.from_embed_types { for i, embed in node.from_embed_types {
embed_sym := g.table.sym(embed) embed_sym := g.table.sym(embed)
embed_name := embed_sym.embed_name() embed_name := embed_sym.embed_name()
if node.expr_type.is_ptr() { is_left_ptr := if i == 0 {
node.expr_type.is_ptr()
} else {
node.from_embed_types[i - 1].is_ptr()
}
if is_left_ptr {
g.write('->') g.write('->')
} else { } else {
g.write('.') g.write('.')
@ -6310,10 +6320,6 @@ fn (mut g Gen) size_of(node ast.SizeOf) {
g.write('sizeof(${util.no_dots(styp)})') g.write('sizeof(${util.no_dots(styp)})')
} }
fn (g &Gen) is_importing_os() bool {
return 'os' in g.table.imports
}
fn (mut g Gen) go_expr(node ast.GoExpr) { fn (mut g Gen) go_expr(node ast.GoExpr) {
line := g.go_before_stmt(0) line := g.go_before_stmt(0)
mut handle := '' mut handle := ''
@ -6797,16 +6803,13 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
} }
else {} else {}
} }
if st_sym.info is ast.Struct { t_methods := g.table.get_embed_methods(st_sym)
for embed in st_sym.info.embeds { for t_method in t_methods {
embed_sym := g.table.sym(embed) if t_method.name !in method_names {
for embed_method in embed_sym.methods { methods << t_method
if embed_method.name !in method_names {
methods << embed_method
}
}
} }
} }
for method in methods { for method in methods {
mut name := method.name mut name := method.name
if inter_info.parent_type.has_flag(.generic) { if inter_info.parent_type.has_flag(.generic) {
@ -6834,6 +6837,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
styp := g.cc_type(method.params[0].typ, true) styp := g.cc_type(method.params[0].typ, true)
mut method_call := '${styp}_$name' mut method_call := '${styp}_$name'
if !method.params[0].typ.is_ptr() { if !method.params[0].typ.is_ptr() {
method_call = '${cctype}_$name'
// inline void Cat_speak_Interface_Animal_method_wrapper(Cat c) { return Cat_speak(*c); } // inline void Cat_speak_Interface_Animal_method_wrapper(Cat c) { return Cat_speak(*c); }
iwpostfix := '_Interface_${interface_name}_method_wrapper' iwpostfix := '_Interface_${interface_name}_method_wrapper'
methods_wrapper.write_string('static inline ${g.typ(method.return_type)} ${cctype}_$name${iwpostfix}(') methods_wrapper.write_string('static inline ${g.typ(method.return_type)} ${cctype}_$name${iwpostfix}(')
@ -6865,9 +6869,13 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
embed_sym := g.table.sym(embed_types.last()) embed_sym := g.table.sym(embed_types.last())
method_name := '${embed_sym.cname}_$method.name' method_name := '${embed_sym.cname}_$method.name'
methods_wrapper.write_string('${method_name}(${fargs[0]}') methods_wrapper.write_string('${method_name}(${fargs[0]}')
for embed in embed_types { for idx_embed, embed in embed_types {
esym := g.table.sym(embed) esym := g.table.sym(embed)
if idx_embed == 0 || embed_types[idx_embed - 1].is_any_kind_of_pointer() {
methods_wrapper.write_string('->$esym.embed_name()') methods_wrapper.write_string('->$esym.embed_name()')
} else {
methods_wrapper.write_string('.$esym.embed_name()')
}
} }
methods_wrapper.writeln('${fargs[1..].join(', ')});') methods_wrapper.writeln('${fargs[1..].join(', ')});')
} else { } else {

View File

@ -0,0 +1,26 @@
fn test_generics_with_complex_nested_generics_type() {
mut buf := []byte{}
initial<string, u64>(buf)
}
fn initial<K, V>(buf []byte) map[K]V {
mut ret := map[K]V{}
for _ in 0 .. 3 {
k := get<K>(buf)
v := get<V>(buf)
ret[k] = v
}
println(ret)
assert '$ret' == "{'get': 22}"
return ret
}
fn get<T>(buf []byte) T {
$if T is string {
return buf.bytestr() + 'get'
} $else $if T is u64 {
return u64(22)
} $else {
panic('oops!')
}
}

View File

@ -0,0 +1,91 @@
fn test_interface_with_multi_nested_embed() {
mut win := &Window{}
mut ll := &LinearLayout{}
mut lbl := &Label{
x: 10
y: 20
}
ll.add(mut lbl)
win.add(mut ll)
win.init()
}
[heap]
pub struct Window {
mut:
initables []&Initable
}
interface Initable {
mut:
init(&Window)
}
pub fn (mut w Window) add(mut initable Initable) {
w.initables << initable
}
interface Container {
mut:
layout()
}
fn (mut w Window) init() {
for wd in w.initables {
if mut wd is Container {
mut c := wd as Container
c.layout()
}
}
}
pub struct Rect {
mut:
x int
y int
}
pub fn (r Rect) get_pos() (int, int) {
return r.x, r.y
}
pub struct LayoutBase {
Rect
}
pub struct Base {
LayoutBase
}
pub fn (mut b Base) init(window &Window) {}
[heap]
pub struct Label {
Base
}
pub interface Layoutable {
get_pos() (int, int)
}
[heap]
pub struct LinearLayout {
Base
mut:
layoutables []Layoutable
}
pub fn (mut ll LinearLayout) add(mut l Layoutable) {
ll.layoutables << l
}
pub fn (mut ll LinearLayout) layout() {
for mut wl in ll.layoutables {
x, y := wl.get_pos()
println('$x, $y')
assert x == 10
assert y == 20
}
}

View File

@ -0,0 +1,57 @@
fn test_interface_with_multi_nested_embed() {
mut ll := &LinearLayout{}
mut lbl := &Label{
x: 10
y: 20
}
ll.add(mut lbl)
println(ll)
assert ll.x == 10
assert ll.y == 20
}
pub struct Rect {
mut:
x int
y int
}
pub fn (r Rect) get_pos() (int, int) {
return r.x, r.y
}
pub struct LayoutBase {
Rect
}
pub struct Base {
LayoutBase
}
pub fn (mut b Base) init() {}
[heap]
pub struct Label {
Base
}
pub interface Layoutable {
get_pos() (int, int)
mut:
init()
}
[heap]
pub struct LinearLayout {
Base
mut:
layoutables []Layoutable
}
pub fn (mut ll LinearLayout) add(mut l Layoutable) {
x, y := l.get_pos()
ll.x += x
ll.y += y
}

View File

@ -0,0 +1,22 @@
struct AA {
b shared BB
}
struct BB {
a &int
}
struct CC {
a BB
}
fn test_struct_shared_field_init() {
a := 3
table := &AA{
b: BB{&a}
}
c := CC{
a: table.b
}
assert *c.a.a == 3
}

View File

@ -7,6 +7,7 @@ import io
const ( const (
sport = 12380 sport = 12380
localserver = 'localhost:$sport'
exit_after_time = 12000 // milliseconds exit_after_time = 12000 // milliseconds
vexe = os.getenv('VEXE') vexe = os.getenv('VEXE')
vweb_logfile = os.getenv('VWEB_LOGFILE') vweb_logfile = os.getenv('VWEB_LOGFILE')
@ -116,7 +117,7 @@ fn assert_common_http_headers(x http.Response) ? {
} }
fn test_http_client_index() ? { fn test_http_client_index() ? {
x := http.get('http://127.0.0.1:$sport/') or { panic(err) } x := http.get('http://$localserver/') or { panic(err) }
assert_common_http_headers(x) ? assert_common_http_headers(x) ?
assert x.header.get(.content_type) ? == 'text/plain' assert x.header.get(.content_type) ? == 'text/plain'
assert x.text == 'Welcome to VWeb' assert x.text == 'Welcome to VWeb'
@ -124,9 +125,9 @@ fn test_http_client_index() ? {
fn test_http_client_404() ? { fn test_http_client_404() ? {
url_404_list := [ url_404_list := [
'http://127.0.0.1:$sport/zxcnbnm', 'http://$localserver/zxcnbnm',
'http://127.0.0.1:$sport/JHKAJA', 'http://$localserver/JHKAJA',
'http://127.0.0.1:$sport/unknown', 'http://$localserver/unknown',
] ]
for url in url_404_list { for url in url_404_list {
res := http.get(url) or { panic(err) } res := http.get(url) or { panic(err) }
@ -135,39 +136,39 @@ fn test_http_client_404() ? {
} }
fn test_http_client_simple() ? { fn test_http_client_simple() ? {
x := http.get('http://127.0.0.1:$sport/simple') or { panic(err) } x := http.get('http://$localserver/simple') or { panic(err) }
assert_common_http_headers(x) ? assert_common_http_headers(x) ?
assert x.header.get(.content_type) ? == 'text/plain' assert x.header.get(.content_type) ? == 'text/plain'
assert x.text == 'A simple result' assert x.text == 'A simple result'
} }
fn test_http_client_html_page() ? { fn test_http_client_html_page() ? {
x := http.get('http://127.0.0.1:$sport/html_page') or { panic(err) } x := http.get('http://$localserver/html_page') or { panic(err) }
assert_common_http_headers(x) ? assert_common_http_headers(x) ?
assert x.header.get(.content_type) ? == 'text/html' assert x.header.get(.content_type) ? == 'text/html'
assert x.text == '<h1>ok</h1>' assert x.text == '<h1>ok</h1>'
} }
fn test_http_client_settings_page() ? { fn test_http_client_settings_page() ? {
x := http.get('http://127.0.0.1:$sport/bilbo/settings') or { panic(err) } x := http.get('http://$localserver/bilbo/settings') or { panic(err) }
assert_common_http_headers(x) ? assert_common_http_headers(x) ?
assert x.text == 'username: bilbo' assert x.text == 'username: bilbo'
// //
y := http.get('http://127.0.0.1:$sport/kent/settings') or { panic(err) } y := http.get('http://$localserver/kent/settings') or { panic(err) }
assert_common_http_headers(y) ? assert_common_http_headers(y) ?
assert y.text == 'username: kent' assert y.text == 'username: kent'
} }
fn test_http_client_user_repo_settings_page() ? { fn test_http_client_user_repo_settings_page() ? {
x := http.get('http://127.0.0.1:$sport/bilbo/gostamp/settings') or { panic(err) } x := http.get('http://$localserver/bilbo/gostamp/settings') or { panic(err) }
assert_common_http_headers(x) ? assert_common_http_headers(x) ?
assert x.text == 'username: bilbo | repository: gostamp' assert x.text == 'username: bilbo | repository: gostamp'
// //
y := http.get('http://127.0.0.1:$sport/kent/golang/settings') or { panic(err) } y := http.get('http://$localserver/kent/golang/settings') or { panic(err) }
assert_common_http_headers(y) ? assert_common_http_headers(y) ?
assert y.text == 'username: kent | repository: golang' assert y.text == 'username: kent | repository: golang'
// //
z := http.get('http://127.0.0.1:$sport/missing/golang/settings') or { panic(err) } z := http.get('http://$localserver/missing/golang/settings') or { panic(err) }
assert z.status() == .not_found assert z.status() == .not_found
} }
@ -182,7 +183,7 @@ fn test_http_client_json_post() ? {
age: 123 age: 123
} }
json_for_ouser := json.encode(ouser) json_for_ouser := json.encode(ouser)
mut x := http.post_json('http://127.0.0.1:$sport/json_echo', json_for_ouser) or { panic(err) } mut x := http.post_json('http://$localserver/json_echo', json_for_ouser) or { panic(err) }
$if debug_net_socket_client ? { $if debug_net_socket_client ? {
eprintln('/json_echo endpoint response: $x') eprintln('/json_echo endpoint response: $x')
} }
@ -191,7 +192,7 @@ fn test_http_client_json_post() ? {
nuser := json.decode(User, x.text) or { User{} } nuser := json.decode(User, x.text) or { User{} }
assert '$ouser' == '$nuser' assert '$ouser' == '$nuser'
// //
x = http.post_json('http://127.0.0.1:$sport/json', json_for_ouser) or { panic(err) } x = http.post_json('http://$localserver/json', json_for_ouser) or { panic(err) }
$if debug_net_socket_client ? { $if debug_net_socket_client ? {
eprintln('/json endpoint response: $x') eprintln('/json endpoint response: $x')
} }
@ -213,7 +214,7 @@ $contents\r
--$boundary--\r --$boundary--\r
' '
mut x := http.fetch( mut x := http.fetch(
url: 'http://127.0.0.1:$sport/form_echo' url: 'http://$localserver/form_echo'
method: .post method: .post
header: http.new_header( header: http.new_header(
key: .content_type key: .content_type
@ -228,7 +229,7 @@ $contents\r
} }
fn test_http_client_shutdown_does_not_work_without_a_cookie() { fn test_http_client_shutdown_does_not_work_without_a_cookie() {
x := http.get('http://127.0.0.1:$sport/shutdown') or { x := http.get('http://$localserver/shutdown') or {
assert err.msg == '' assert err.msg == ''
return return
} }
@ -240,7 +241,7 @@ fn testsuite_end() {
// This test is guaranteed to be called last. // This test is guaranteed to be called last.
// It sends a request to the server to shutdown. // It sends a request to the server to shutdown.
x := http.fetch( x := http.fetch(
url: 'http://127.0.0.1:$sport/shutdown' url: 'http://$localserver/shutdown'
method: .get method: .get
cookies: { cookies: {
'skey': 'superman' 'skey': 'superman'
@ -268,7 +269,7 @@ fn simple_tcp_client(config SimpleTcpClientConfig) ?string {
mut tries := 0 mut tries := 0
for tries < config.retries { for tries < config.retries {
tries++ tries++
client = net.dial_tcp('127.0.0.1:$sport') or { client = net.dial_tcp(localserver) or {
if tries > config.retries { if tries > config.retries {
return err return err
} }

View File

@ -43,9 +43,8 @@ fn main() {
timeout: timeout timeout: timeout
global_config: config global_config: config
} }
eprintln('>> webserver: started on http://127.0.0.1:$app.port/ , with maximum runtime of $app.timeout milliseconds.') eprintln('>> webserver: started on http://localhost:$app.port/ , with maximum runtime of $app.timeout milliseconds.')
// vweb.run<App>(mut app, http_port) vweb.run_at(app, 'localhost', http_port)
vweb.run(app, http_port)
} }
// pub fn (mut app App) init_server() { // pub fn (mut app App) init_server() {

View File

@ -376,10 +376,16 @@ interface DbInterface {
db voidptr db voidptr
} }
// run_app // run - start a new VWeb server, listening to all available addresses, at the specified `port`
[manualfree]
pub fn run<T>(global_app &T, port int) { pub fn run<T>(global_app &T, port int) {
mut l := net.listen_tcp(.ip6, ':$port') or { panic('failed to listen $err.code $err') } run_at<T>(global_app, '', port)
}
// run_at - start a new VWeb server, listening only on a specific address `host`, at the specified `port`
// Example: `vweb.run_at(app, 'localhost', 8099)`
[manualfree]
pub fn run_at<T>(global_app &T, host string, port int) {
mut l := net.listen_tcp(.ip, '$host:$port') or { panic('failed to listen $err.code $err') }
// Parsing methods attributes // Parsing methods attributes
mut routes := map[string]Route{} mut routes := map[string]Route{}