Merge branch 'master' into string-index-funcs-return-opt-int
commit
59082fee32
|
@ -40,6 +40,7 @@ jobs:
|
|||
|
||||
- name: Build sdl examples
|
||||
run: |
|
||||
v shader sdl/examples/sdl_opengl_and_sokol
|
||||
for example in sdl/examples/*; do
|
||||
echo "v $example"
|
||||
v "$example";
|
||||
|
|
25
README.md
25
README.md
|
@ -60,23 +60,40 @@ Unlike many other languages, V is not going to be always changing, with new feat
|
|||
being introduced and old features modified. It is always going to be a small and simple
|
||||
language, very similar to the way it is right now.
|
||||
|
||||
## Installing V from source
|
||||
## Installing V - from source *(preferred method)*
|
||||
|
||||
### Linux, macOS, Windows, *BSD, Solaris, WSL, Android, Raspbian
|
||||
### Linux, macOS, Windows, *BSD, Solaris, WSL, Android, etc.
|
||||
|
||||
Usually installing V is quite simple if you have an environment that already has a
|
||||
functional `git` installation.
|
||||
|
||||
* *(* ***PLEASE NOTE:*** *If you run into any trouble or you have a different operating
|
||||
system or Linux distribution that doesn't install or work immediately, please see
|
||||
[Installation Issues](https://github.com/vlang/v/discussions/categories/installation-issues)
|
||||
and search for your OS and problem. If you can't find your problem, please add it to an
|
||||
existing discussion if one exists for your OS, or create a new one if a main discussion
|
||||
doesn't yet exist for your OS.)*
|
||||
|
||||
|
||||
To get started, simply try to execute the following in your terminal/shell:
|
||||
```bash
|
||||
git clone https://github.com/vlang/v
|
||||
cd v
|
||||
make
|
||||
# HINT: Using Windows?: run make.bat in the cmd.exe shell
|
||||
```
|
||||
|
||||
That's it! Now you have a V executable at `[path to V repo]/v`.
|
||||
That should be it and you should find your V executable at `[path to V repo]/v`.
|
||||
`[path to V repo]` can be anywhere.
|
||||
|
||||
(On Windows `make` means running `make.bat`, so make sure you use `cmd.exe`)
|
||||
(As in the hint above, on Windows `make` means running `make.bat`, so make sure you use
|
||||
the `cmd.exe` terminal.)
|
||||
|
||||
Now you can try `./v run examples/hello_world.v` (`v.exe` on Windows).
|
||||
|
||||
* *Trouble? Please see the note above and link to
|
||||
[Installation Issues](https://github.com/vlang/v/discussions/categories/installation-issues) for help.*
|
||||
|
||||
V is constantly being updated. To update V, simply run:
|
||||
|
||||
```bash
|
||||
|
|
|
@ -118,7 +118,7 @@ fn main() {
|
|||
build_output := get_v_build_output(is_verbose, is_yes, file_path)
|
||||
// ask the user if he wants to submit even after an error
|
||||
if !is_yes && (vdoctor_output == '' || file_content == '' || build_output == '') {
|
||||
confirm_or_exit('An error occured retrieving the information, do you want to continue?')
|
||||
confirm_or_exit('An error occurred retrieving the information, do you want to continue?')
|
||||
}
|
||||
|
||||
expected_result := readline.read_line('What did you expect to see? ') or {
|
||||
|
|
|
@ -27,6 +27,8 @@ struct FormatOptions {
|
|||
is_verify bool // exit(1) if the file is not vfmt'ed
|
||||
is_worker bool // true *only* in the worker processes. Note: workers can crash.
|
||||
is_backup bool // make a `file.v.bak` copy *before* overwriting a `file.v` in place with `-w`
|
||||
mut:
|
||||
diff_cmd string // filled in when -diff or -verify is passed
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -201,36 +203,23 @@ fn print_compiler_options(compiler_params &pref.Preferences) {
|
|||
eprintln(' is_script: $compiler_params.is_script ')
|
||||
}
|
||||
|
||||
fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path string) ? {
|
||||
fn (mut foptions FormatOptions) find_diff_cmd() string {
|
||||
if foptions.diff_cmd != '' {
|
||||
return foptions.diff_cmd
|
||||
}
|
||||
if foptions.is_verify || foptions.is_diff {
|
||||
foptions.diff_cmd = diff.find_working_diff_command() or {
|
||||
eprintln(err)
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
return foptions.diff_cmd
|
||||
}
|
||||
|
||||
fn (mut foptions FormatOptions) post_process_file(file string, formatted_file_path string) ? {
|
||||
if formatted_file_path.len == 0 {
|
||||
return
|
||||
}
|
||||
if foptions.is_diff {
|
||||
diff_cmd := diff.find_working_diff_command() or {
|
||||
eprintln(err)
|
||||
return
|
||||
}
|
||||
if foptions.is_verbose {
|
||||
eprintln('Using diff command: $diff_cmd')
|
||||
}
|
||||
diff := diff.color_compare_files(diff_cmd, file, formatted_file_path)
|
||||
if diff.len > 0 {
|
||||
println(diff)
|
||||
}
|
||||
return
|
||||
}
|
||||
if foptions.is_verify {
|
||||
diff_cmd := diff.find_working_diff_command() or {
|
||||
eprintln(err)
|
||||
return
|
||||
}
|
||||
x := diff.color_compare_files(diff_cmd, file, formatted_file_path)
|
||||
if x.len != 0 {
|
||||
println("$file is not vfmt'ed")
|
||||
return error('')
|
||||
}
|
||||
return
|
||||
}
|
||||
fc := os.read_file(file) or {
|
||||
eprintln('File $file could not be read')
|
||||
return
|
||||
|
@ -240,6 +229,31 @@ fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path
|
|||
return
|
||||
}
|
||||
is_formatted_different := fc != formatted_fc
|
||||
if foptions.is_diff {
|
||||
if !is_formatted_different {
|
||||
return
|
||||
}
|
||||
diff_cmd := foptions.find_diff_cmd()
|
||||
if foptions.is_verbose {
|
||||
eprintln('Using diff command: $diff_cmd')
|
||||
}
|
||||
diff := diff.color_compare_files(diff_cmd, file, formatted_file_path)
|
||||
if diff.len > 0 {
|
||||
println(diff)
|
||||
}
|
||||
return
|
||||
}
|
||||
if foptions.is_verify {
|
||||
if !is_formatted_different {
|
||||
return
|
||||
}
|
||||
x := diff.color_compare_files(foptions.find_diff_cmd(), file, formatted_file_path)
|
||||
if x.len != 0 {
|
||||
println("$file is not vfmt'ed")
|
||||
return error('')
|
||||
}
|
||||
return
|
||||
}
|
||||
if foptions.is_c {
|
||||
if is_formatted_different {
|
||||
eprintln('File is not formatted: $file')
|
||||
|
|
|
@ -354,7 +354,11 @@ fn vexe() string {
|
|||
}
|
||||
|
||||
fn new_config(root_path string, toml_config string) ?Config {
|
||||
doc := toml.parse(toml_config) ?
|
||||
doc := if os.is_file(toml_config) {
|
||||
toml.parse_file(toml_config) ?
|
||||
} else {
|
||||
toml.parse_text(toml_config) ?
|
||||
}
|
||||
|
||||
path := os.real_path(root_path).trim_right('/')
|
||||
|
||||
|
|
|
@ -208,11 +208,21 @@ fn (mut r Repl) parse_import(line string) {
|
|||
}
|
||||
}
|
||||
|
||||
fn highlight_console_command(command string) string {
|
||||
return term.bright_white(term.bright_bg_black(' $command '))
|
||||
}
|
||||
|
||||
fn highlight_repl_command(command string) string {
|
||||
return term.bright_white(term.bg_blue(' $command '))
|
||||
}
|
||||
|
||||
fn print_welcome_screen() {
|
||||
cmd_exit := term.highlight_command('exit')
|
||||
cmd_help := term.highlight_command('v help')
|
||||
file_main := term.highlight_command('main.v')
|
||||
cmd_run := term.highlight_command('v run main.v')
|
||||
cmd_exit := highlight_repl_command('exit')
|
||||
cmd_list := highlight_repl_command('list')
|
||||
cmd_help := highlight_repl_command('help')
|
||||
cmd_v_help := highlight_console_command('v help')
|
||||
cmd_v_run := highlight_console_command('v run main.v')
|
||||
file_main := highlight_console_command('main.v')
|
||||
vbar := term.bright_green('|')
|
||||
width, _ := term.get_terminal_size() // get the size of the terminal
|
||||
vlogo := [
|
||||
|
@ -224,11 +234,11 @@ fn print_welcome_screen() {
|
|||
term.bright_blue(r' \__/ '),
|
||||
]
|
||||
help_text := [
|
||||
'Welcome to the V REPL (for help with V itself, type $cmd_exit, then run $cmd_help).',
|
||||
'Welcome to the V REPL (for help with V itself, type $cmd_exit, then run $cmd_v_help).',
|
||||
'Note: the REPL is highly experimental. For best V experience, use a text editor, ',
|
||||
'save your code in a $file_main file and execute: $cmd_run',
|
||||
version.full_v_version(false),
|
||||
'Use Ctrl-C or ${term.highlight_command('exit')} to exit, or ${term.highlight_command('help')} to see other available commands',
|
||||
'save your code in a $file_main file and execute: $cmd_v_run',
|
||||
'${version.full_v_version(false)} . Use $cmd_list to see the accumulated program so far.',
|
||||
'Use Ctrl-C or $cmd_exit to exit, or $cmd_help to see other available commands.',
|
||||
]
|
||||
if width >= 97 {
|
||||
eprintln('${vlogo[0]}')
|
||||
|
@ -480,6 +490,9 @@ fn main() {
|
|||
println(' ... where vexepath is the full path to the v executable file')
|
||||
return
|
||||
}
|
||||
if !is_stdin_a_pipe {
|
||||
os.setenv('VCOLORS', 'always', true)
|
||||
}
|
||||
run_repl(replfolder, replprefix)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ see also `v help build`.
|
|||
|
||||
-cstrict
|
||||
Turn on additional C warnings. This slows down compilation
|
||||
slightly (~10-10% for gcc), but sometimes provides better diagnosis.
|
||||
slightly (~10% for gcc), but sometimes provides better diagnosis.
|
||||
|
||||
-showcc
|
||||
Prints the C command that is used to build the program.
|
||||
|
@ -97,7 +97,8 @@ see also `v help build`.
|
|||
systems also (although we do not test it as regularly as for the above):
|
||||
`android`, `ios`,
|
||||
`freebsd`, `openbsd`, `netbsd`, `dragonfly`,
|
||||
`solaris`, `serenity`, `haiku`, `vinix`
|
||||
`solaris`, `serenity`, `haiku`, `vinix`,
|
||||
`wasm32`, `wasm32-wasi`, `wasm32-emscripten`
|
||||
|
||||
Note that V has the concept of platform files, i.e. files ending
|
||||
with `_platform.c.v`, and usually only the matching files are used in
|
||||
|
|
|
@ -624,6 +624,9 @@ println('[${int(x):010}]') // pad with zeros on the left => [0000000123]
|
|||
println('[${int(x):b}]') // output as binary => [1111011]
|
||||
println('[${int(x):o}]') // output as octal => [173]
|
||||
println('[${int(x):X}]') // output as uppercase hex => [7B]
|
||||
|
||||
println('[${10.0000:.2}]') // remove insignificant 0s at the end => [10]
|
||||
println('[${10.0000:.2f}]') // do show the 0s at the end, even though they do not change the number => [10.00]
|
||||
```
|
||||
|
||||
### String operators
|
||||
|
|
|
@ -38,7 +38,7 @@ hosts = [
|
|||
]'
|
||||
|
||||
fn main() {
|
||||
doc := toml.parse(toml_text) or { panic(err) }
|
||||
doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
title := doc.value('title').string()
|
||||
println('title: "$title"')
|
||||
ip := doc.value('servers.alpha.ip').string()
|
||||
|
|
|
@ -144,7 +144,7 @@ fn main() {
|
|||
// TTF render 0 Frame counter
|
||||
app.ttf_render << &ttf.TTF_render_Sokol{
|
||||
bmp: &ttf.BitMap{
|
||||
tf: &(app.tf[0])
|
||||
tf: &app.tf[0]
|
||||
buf: unsafe { malloc_noscan(32000000) }
|
||||
buf_size: (32000000)
|
||||
color: 0xFF0000FF
|
||||
|
@ -155,7 +155,7 @@ fn main() {
|
|||
// TTF render 1 Text Block
|
||||
app.ttf_render << &ttf.TTF_render_Sokol{
|
||||
bmp: &ttf.BitMap{
|
||||
tf: &(app.tf[1])
|
||||
tf: &app.tf[1]
|
||||
// color : 0xFF0000_10
|
||||
// style: .raw
|
||||
// use_font_metrics: true
|
||||
|
@ -164,7 +164,7 @@ fn main() {
|
|||
// TTF mouse position render
|
||||
app.ttf_render << &ttf.TTF_render_Sokol{
|
||||
bmp: &ttf.BitMap{
|
||||
tf: &(app.tf[0])
|
||||
tf: &app.tf[0]
|
||||
}
|
||||
}
|
||||
// setup sokol_gfx
|
||||
|
|
|
@ -17,3 +17,9 @@ img.logo {
|
|||
float: left;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
html {
|
||||
display:block;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<html>
|
||||
<header>
|
||||
<title>@title</title>
|
||||
<style>html{display:none}</style>
|
||||
@css 'index.css'
|
||||
</header>
|
||||
<body>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module main
|
||||
|
||||
import os
|
||||
import vweb
|
||||
// import vweb.assets
|
||||
import time
|
||||
|
@ -16,6 +17,7 @@ fn main() {
|
|||
mut app := &App{}
|
||||
app.serve_static('/favicon.ico', 'favicon.ico')
|
||||
// Automatically make available known static mime types found in given directory.
|
||||
os.chdir(os.dir(os.executable())) ?
|
||||
app.handle_static('assets', true)
|
||||
vweb.run(app, port)
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ fn (mut a array) ensure_cap(required int) {
|
|||
cap *= 2
|
||||
}
|
||||
new_size := cap * a.element_size
|
||||
new_data := vcalloc(new_size)
|
||||
new_data := unsafe { malloc(new_size) }
|
||||
if a.data != voidptr(0) {
|
||||
unsafe { vmemcpy(new_data, a.data, a.len * a.element_size) }
|
||||
// TODO: the old data may be leaked when no GC is used (ref-counting?)
|
||||
|
@ -402,12 +402,13 @@ pub fn (mut a array) pop() voidptr {
|
|||
a.len = new_len
|
||||
// Note: a.cap is not changed here *on purpose*, so that
|
||||
// further << ops on that array will be more efficient.
|
||||
return unsafe { memdup(last_elem, a.element_size) }
|
||||
return last_elem
|
||||
}
|
||||
|
||||
// delete_last efficiently deletes the last element of the array.
|
||||
// It does it simply by reducing the length of the array by 1.
|
||||
// If the array is empty, this will panic.
|
||||
// See also: [trim](#array.trim)
|
||||
pub fn (mut a array) delete_last() {
|
||||
// copy pasting code for performance
|
||||
$if !no_bounds_checking ? {
|
||||
|
|
|
@ -660,7 +660,7 @@ pub fn str_intp(data_len int, in_data voidptr) string {
|
|||
mut res := strings.new_builder(256)
|
||||
input_base := &StrIntpData(in_data)
|
||||
for i := 0; i < data_len; i++ {
|
||||
data := unsafe { &(input_base[i]) }
|
||||
data := unsafe { &input_base[i] }
|
||||
// avoid empty strings
|
||||
if data.str.len != 0 {
|
||||
res.write_string(data.str)
|
||||
|
|
|
@ -5,7 +5,7 @@ module builtin
|
|||
[unsafe]
|
||||
pub fn __malloc(size usize) voidptr {
|
||||
unsafe {
|
||||
return malloc(int(size))
|
||||
return C.malloc(int(size))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -467,3 +467,20 @@ pub fn hex(len int) string {
|
|||
pub fn ascii(len int) string {
|
||||
return string_from_set(rand.ascii_chars, len)
|
||||
}
|
||||
|
||||
// shuffle randomly permutates the elements in `a`.
|
||||
pub fn shuffle<T>(mut a []T) {
|
||||
len := a.len
|
||||
for i in 0 .. len {
|
||||
si := i + intn(len - i) or { len }
|
||||
a[si], a[i] = a[i], a[si]
|
||||
}
|
||||
}
|
||||
|
||||
// shuffle_clone returns a random permutation of the elements in `a`.
|
||||
// The permutation is done on a fresh clone of `a`, so `a` remains unchanged.
|
||||
pub fn shuffle_clone<T>(a []T) []T {
|
||||
mut res := a.clone()
|
||||
shuffle(mut res)
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -317,3 +317,55 @@ fn test_new_global_rng() {
|
|||
|
||||
rand.set_rng(old)
|
||||
}
|
||||
|
||||
fn test_shuffle() {
|
||||
mut arrays := [][]int{}
|
||||
arrays << [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
arrays << [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
||||
for seed in seeds {
|
||||
a := get_n_random_ints(seed, 10)
|
||||
arrays << a
|
||||
}
|
||||
//
|
||||
mut digits := []map[int]int{len: 10}
|
||||
for digit in 0 .. 10 {
|
||||
digits[digit] = {}
|
||||
for idx in 0 .. 10 {
|
||||
digits[digit][idx] = 0
|
||||
}
|
||||
}
|
||||
for mut a in arrays {
|
||||
o := a.clone()
|
||||
for _ in 0 .. 100 {
|
||||
rand.shuffle(mut a)
|
||||
assert *a != o
|
||||
for idx in 0 .. 10 {
|
||||
digits[idx][a[idx]]++
|
||||
}
|
||||
}
|
||||
}
|
||||
for digit in 1 .. 10 {
|
||||
assert digits[0] != digits[digit]
|
||||
}
|
||||
for digit in 0 .. 10 {
|
||||
for idx in 0 .. 10 {
|
||||
assert digits[digit][idx] > 10
|
||||
}
|
||||
// eprintln('digits[$digit]: ${digits[digit]}')
|
||||
}
|
||||
}
|
||||
|
||||
fn test_shuffle_clone() {
|
||||
original := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
mut a := original.clone()
|
||||
mut results := [][]int{}
|
||||
for _ in 0 .. 10 {
|
||||
results << rand.shuffle_clone(a)
|
||||
}
|
||||
assert original == a
|
||||
for idx in 1 .. 10 {
|
||||
assert results[idx].len == 10
|
||||
assert results[idx] != results[0]
|
||||
assert results[idx] != original
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,29 +158,36 @@ pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
|
|||
|
||||
// get sign and decimal parts
|
||||
for c in s {
|
||||
if c == `-` {
|
||||
sgn = -1
|
||||
i++
|
||||
} else if c == `+` {
|
||||
sgn = 1
|
||||
i++
|
||||
} else if c >= `0` && c <= `9` {
|
||||
b[i1] = c
|
||||
i1++
|
||||
i++
|
||||
} else if c == `.` {
|
||||
if sgn > 0 {
|
||||
d_pos = i
|
||||
} else {
|
||||
d_pos = i - 1
|
||||
match c {
|
||||
`-` {
|
||||
sgn = -1
|
||||
i++
|
||||
}
|
||||
`+` {
|
||||
sgn = 1
|
||||
i++
|
||||
}
|
||||
`0`...`9` {
|
||||
b[i1] = c
|
||||
i1++
|
||||
i++
|
||||
}
|
||||
`.` {
|
||||
if sgn > 0 {
|
||||
d_pos = i
|
||||
} else {
|
||||
d_pos = i - 1
|
||||
}
|
||||
i++
|
||||
}
|
||||
`e` {
|
||||
i++
|
||||
break
|
||||
}
|
||||
else {
|
||||
s.free()
|
||||
return '[Float conversion error!!]'
|
||||
}
|
||||
i++
|
||||
} else if c == `e` {
|
||||
i++
|
||||
break
|
||||
} else {
|
||||
s.free()
|
||||
return '[Float conversion error!!]'
|
||||
}
|
||||
}
|
||||
b[i1] = 0
|
||||
|
@ -274,10 +281,13 @@ pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
|
|||
|
||||
// println("r_i-d_pos: ${r_i - d_pos}")
|
||||
if dot_res_sp >= 0 {
|
||||
if (r_i - dot_res_sp) > dec_digit {
|
||||
r_i = dot_res_sp + dec_digit + 1
|
||||
}
|
||||
r_i = dot_res_sp + dec_digit + 1
|
||||
res[r_i] = 0
|
||||
for c1 in 1 .. dec_digit + 1 {
|
||||
if res[r_i - c1] == 0 {
|
||||
res[r_i - c1] = `0`
|
||||
}
|
||||
}
|
||||
// println("result: [${tos(&res[0],r_i)}]")
|
||||
tmp_res := tos(res.data, r_i).clone()
|
||||
res.free()
|
||||
|
|
|
@ -176,6 +176,28 @@ pub fn (mut b Builder) str() string {
|
|||
return s
|
||||
}
|
||||
|
||||
// ensure_cap ensures that the buffer has enough space for at least `n` bytes by growing the buffer if necessary
|
||||
pub fn (mut b Builder) ensure_cap(n int) {
|
||||
// code adapted from vlib/builtin/array.v
|
||||
if n <= b.cap {
|
||||
return
|
||||
}
|
||||
|
||||
new_data := vcalloc(n * b.element_size)
|
||||
if b.data != voidptr(0) {
|
||||
unsafe { vmemcpy(new_data, b.data, b.len * b.element_size) }
|
||||
// TODO: the old data may be leaked when no GC is used (ref-counting?)
|
||||
if b.flags.has(.noslices) {
|
||||
unsafe { free(b.data) }
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
b.data = new_data
|
||||
b.offset = 0
|
||||
b.cap = n
|
||||
}
|
||||
}
|
||||
|
||||
// free is for manually freeing the contents of the buffer
|
||||
[unsafe]
|
||||
pub fn (mut b Builder) free() {
|
||||
|
|
|
@ -112,3 +112,18 @@ fn test_write_runes() {
|
|||
x := sb.str()
|
||||
assert x == 'hello world'
|
||||
}
|
||||
|
||||
fn test_ensure_cap() {
|
||||
mut sb := strings.new_builder(0)
|
||||
assert sb.cap == 0
|
||||
sb.ensure_cap(10)
|
||||
assert sb.cap == 10
|
||||
sb.ensure_cap(10)
|
||||
assert sb.cap == 10
|
||||
sb.ensure_cap(15)
|
||||
assert sb.cap == 15
|
||||
sb.ensure_cap(10)
|
||||
assert sb.cap == 15
|
||||
sb.ensure_cap(-1)
|
||||
assert sb.cap == 15
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
module sync
|
||||
|
||||
fn C.pthread_self() usize
|
||||
|
||||
// thread_id returns a unique identifier for the caller thread.
|
||||
// All *currently* running threads in the same process, will have *different* thread identifiers.
|
||||
// Note: if a thread finishes, and another starts, the identifier of the old thread may be
|
||||
// reused for the newly started thread.
|
||||
// In other words, thread IDs are guaranteed to be unique only within a process.
|
||||
// A thread ID may be reused after a terminated thread has been joined (with `t.wait()`),
|
||||
// or when the thread has terminated.
|
||||
|
||||
pub fn thread_id() u64 {
|
||||
return u64(C.pthread_self())
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import sync
|
||||
|
||||
fn simple_thread() u64 {
|
||||
tid := sync.thread_id()
|
||||
eprintln('simple_thread thread_id: $tid.hex()')
|
||||
return tid
|
||||
}
|
||||
|
||||
fn test_sync_thread_id() {
|
||||
mtid := sync.thread_id()
|
||||
eprintln('main thread_id: $sync.thread_id().hex()')
|
||||
x := go simple_thread()
|
||||
y := go simple_thread()
|
||||
xtid := x.wait()
|
||||
ytid := y.wait()
|
||||
eprintln('main thread_id: $sync.thread_id().hex()')
|
||||
dump(xtid.hex())
|
||||
dump(ytid.hex())
|
||||
assert mtid != xtid
|
||||
assert mtid != ytid
|
||||
assert xtid != ytid
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
module sync
|
||||
|
||||
fn C.GetCurrentThreadId() u32
|
||||
|
||||
pub fn thread_id() u64 {
|
||||
return u64(C.GetCurrentThreadId())
|
||||
}
|
|
@ -8,7 +8,8 @@ Parsing files or `string`s containing TOML is easy.
|
|||
|
||||
Simply import the `toml` module and do:
|
||||
```v ignore
|
||||
doc := toml.parse(<file path or string>) or { panic(err) }
|
||||
doc1 := toml.parse_text(<string content>) or { panic(err) }
|
||||
doc2 := toml.parse_file(<file path>) or { panic(err) }
|
||||
```
|
||||
|
||||
## Example
|
||||
|
@ -54,7 +55,7 @@ hosts = [
|
|||
]'
|
||||
|
||||
fn main() {
|
||||
doc := toml.parse(toml_text) or { panic(err) }
|
||||
doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
title := doc.value('title').string()
|
||||
println('title: "$title"')
|
||||
ip := doc.value('servers.alpha.ip').string()
|
||||
|
@ -91,7 +92,7 @@ array = [
|
|||
]
|
||||
'
|
||||
|
||||
doc := toml.parse(toml_text) or { panic(err) }
|
||||
doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
|
||||
assert doc.value('val').bool() == true
|
||||
assert doc.value('table.array[0].a').string() == 'A'
|
||||
|
@ -142,6 +143,6 @@ array = [
|
|||
]
|
||||
'
|
||||
|
||||
doc := toml.parse(toml_text) or { panic(err) }
|
||||
doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
assert to.json(doc) == '{ "val": true, "table": { "array": [ { "a": "A" }, { "b": "B" } ] } }'
|
||||
```
|
||||
|
|
|
@ -415,7 +415,7 @@ pub fn (c Checker) check_quoted(q ast.Quoted) ? {
|
|||
// \UXXXXXXXX - Unicode (U+XXXXXXXX)
|
||||
fn (c Checker) check_quoted_escapes(q ast.Quoted) ? {
|
||||
// Setup a scanner in stack memory for easier navigation.
|
||||
mut s := scanner.new_simple(q.text) ?
|
||||
mut s := scanner.new_simple_text(q.text) ?
|
||||
|
||||
// See https://toml.io/en/v1.0.0#string for more info on string types.
|
||||
is_basic := q.quote == `\"`
|
||||
|
@ -552,7 +552,7 @@ fn (c Checker) check_unicode_escape(esc_unicode string) ? {
|
|||
pub fn (c Checker) check_comment(comment ast.Comment) ? {
|
||||
lit := comment.text
|
||||
// Setup a scanner in stack memory for easier navigation.
|
||||
mut s := scanner.new_simple(lit) ?
|
||||
mut s := scanner.new_simple_text(lit) ?
|
||||
for {
|
||||
ch := s.next()
|
||||
if ch == scanner.end_of_text {
|
||||
|
|
|
@ -84,7 +84,7 @@ pub fn decode_quoted_escapes(mut q ast.Quoted) ? {
|
|||
return
|
||||
}
|
||||
|
||||
mut s := scanner.new_simple(q.text) ?
|
||||
mut s := scanner.new_simple_text(q.text) ?
|
||||
q.text = q.text.replace('\\"', '"')
|
||||
|
||||
for {
|
||||
|
|
|
@ -15,6 +15,10 @@ pub:
|
|||
|
||||
// auto_config returns an, automatic determined, input Config based on heuristics
|
||||
// found in `toml`
|
||||
// One example of several of why it's deprecated:
|
||||
// https://discord.com/channels/592103645835821068/592114487759470596/954101934988615721
|
||||
[deprecated: 'will be removed and not replaced due to flaky heuristics that leads to hard to find bugs']
|
||||
[deprecated_after: '2022-06-18']
|
||||
pub fn auto_config(toml string) ?Config {
|
||||
mut config := Config{}
|
||||
if !toml.contains('\n') && os.is_file(toml) {
|
||||
|
@ -32,7 +36,7 @@ pub fn auto_config(toml string) ?Config {
|
|||
|
||||
// validate returns an optional error if more than one of the fields
|
||||
// in `Config` has a non-default value (empty string).
|
||||
pub fn (c Config) validate() ? {
|
||||
fn (c Config) validate() ? {
|
||||
if c.file_path != '' && c.text != '' {
|
||||
error(@MOD + '.' + @FN +
|
||||
' ${typeof(c).name} should contain only one of the fields `file_path` OR `text` filled out')
|
||||
|
@ -42,9 +46,12 @@ pub fn (c Config) validate() ? {
|
|||
}
|
||||
}
|
||||
|
||||
// read_input returns either Config.text or the read file contents of Config.file_path
|
||||
// depending on which one is not empty.
|
||||
pub fn (c Config) read_input() ?string {
|
||||
c.validate() ?
|
||||
mut text := c.text
|
||||
if os.is_file(c.file_path) {
|
||||
if text == '' && os.is_file(c.file_path) {
|
||||
text = os.read_file(c.file_path) or {
|
||||
return error(@MOD + '.' + @STRUCT + '.' + @FN +
|
||||
' Could not read "$c.file_path": "$err.msg()"')
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// that can be found in the LICENSE file.
|
||||
module scanner
|
||||
|
||||
import os
|
||||
import math
|
||||
import toml.input
|
||||
import toml.token
|
||||
|
@ -47,28 +46,47 @@ pub:
|
|||
tokenize_formatting bool = true // if true, generate tokens for `\n`, ` `, `\t`, `\r` etc.
|
||||
}
|
||||
|
||||
// new_scanner returns a new *heap* allocated `Scanner` instance.
|
||||
// new_scanner returns a new *heap* allocated `Scanner` instance, based on the file in config.input.file_path,
|
||||
// or based on the text in config.input.text .
|
||||
pub fn new_scanner(config Config) ?&Scanner {
|
||||
config.input.validate() ?
|
||||
mut text := config.input.text
|
||||
file_path := config.input.file_path
|
||||
if os.is_file(file_path) {
|
||||
text = os.read_file(file_path) or {
|
||||
return error(@MOD + '.' + @STRUCT + '.' + @FN +
|
||||
' Could not read "$file_path": "$err.msg()"')
|
||||
}
|
||||
}
|
||||
mut s := &Scanner{
|
||||
config: config
|
||||
text: text
|
||||
text: config.input.read_input() ?
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// returns a new *stack* allocated `Scanner` instance.
|
||||
pub fn new_simple(toml_input string) ?Scanner {
|
||||
// new_simple returns a new *stack* allocated `Scanner` instance.
|
||||
pub fn new_simple(config Config) ?Scanner {
|
||||
return Scanner{
|
||||
config: config
|
||||
text: config.input.read_input() ?
|
||||
}
|
||||
}
|
||||
|
||||
// new_simple_text returns a new *stack* allocated `Scanner` instance
|
||||
// ready for parsing TOML in `text`.
|
||||
pub fn new_simple_text(text string) ?Scanner {
|
||||
in_config := input.Config{
|
||||
text: text
|
||||
}
|
||||
config := Config{
|
||||
input: input.auto_config(toml_input) ?
|
||||
input: in_config
|
||||
}
|
||||
return Scanner{
|
||||
config: config
|
||||
text: config.input.read_input() ?
|
||||
}
|
||||
}
|
||||
|
||||
// new_simple_file returns a new *stack* allocated `Scanner` instance
|
||||
// ready for parsing TOML in file read from `path`.
|
||||
pub fn new_simple_file(path string) ?Scanner {
|
||||
in_config := input.Config{
|
||||
file_path: path
|
||||
}
|
||||
config := Config{
|
||||
input: in_config
|
||||
}
|
||||
return Scanner{
|
||||
config: config
|
||||
|
|
|
@ -18,7 +18,7 @@ color = "gray"'
|
|||
)
|
||||
|
||||
fn test_tables() {
|
||||
mut toml_doc := toml.parse(toml_table_text) or { panic(err) }
|
||||
mut toml_doc := toml.parse_text(toml_table_text) or { panic(err) }
|
||||
|
||||
toml_json := to.json(toml_doc)
|
||||
|
||||
|
|
|
@ -22,13 +22,13 @@ name = "Born in the USA"
|
|||
name = "Dancing in the Dark"'
|
||||
)
|
||||
|
||||
fn test_nested_array_of_tables() {
|
||||
mut toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
const fprefix = os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))
|
||||
|
||||
fn test_nested_array_of_tables() ? {
|
||||
mut toml_doc := toml.parse_text(toml_text) ?
|
||||
|
||||
toml_json := to.json(toml_doc)
|
||||
|
||||
eprintln(toml_json)
|
||||
assert toml_json == os.read_file(
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.out') or { panic(err) }
|
||||
|
||||
assert toml_json == os.read_file(fprefix + '.out') ?
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ const (
|
|||
)
|
||||
|
||||
fn test_nested_array_of_tables() {
|
||||
mut toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
mut toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
|
||||
toml_json := to.json(toml_doc)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ fn test_array_of_tables_edge_case_file() {
|
|||
toml_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.toml'
|
||||
toml_doc := toml.parse(toml_file) or { panic(err) }
|
||||
toml_doc := toml.parse_file(toml_file) or { panic(err) }
|
||||
|
||||
toml_json := to.json(toml_doc)
|
||||
out_file :=
|
||||
|
|
|
@ -2,17 +2,14 @@ import os
|
|||
import toml
|
||||
import toml.to
|
||||
|
||||
fn test_array_of_tables_edge_case_file() {
|
||||
toml_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.toml'
|
||||
toml_doc := toml.parse(toml_file) or { panic(err) }
|
||||
const fprefix = os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))
|
||||
|
||||
fn test_array_of_tables_edge_case_file() ? {
|
||||
toml_doc := toml.parse_file(os.real_path(fprefix + '.toml')) ?
|
||||
|
||||
toml_json := to.json(toml_doc)
|
||||
out_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.out'
|
||||
out_file_json := os.read_file(out_file) or { panic(err) }
|
||||
|
||||
out_file_json := os.read_file(os.real_path(fprefix + '.out')) ?
|
||||
println(toml_json)
|
||||
assert toml_json == out_file_json
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ hosts = [
|
|||
]'
|
||||
|
||||
fn test_parse_compact_text() {
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
|
||||
title := toml_doc.value('title')
|
||||
assert title == toml.Any('TOML Example')
|
||||
|
|
|
@ -4,7 +4,7 @@ fn test_crlf() {
|
|||
str_value := 'test string'
|
||||
mut toml_txt := 'crlf_string = "test string"\r\n
|
||||
# Comment with CRLF is not allowed'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value := toml_doc.value('crlf_string')
|
||||
assert value == toml.Any(str_value)
|
||||
|
@ -16,7 +16,7 @@ fn test_crlf_is_parsable_just_like_lf() ? {
|
|||
crlf_content := '# a comment\r\ntitle = "TOML Example"\r\n[database]\r\nserver = "192.168.1.1"\r\nports = [ 8000, 8001, 8002 ]\r\n'
|
||||
all := [crlf_content, crlf_content.replace('\r\n', '\n')]
|
||||
for content in all {
|
||||
res := toml.parse(content) ?
|
||||
res := toml.parse_text(content) ?
|
||||
assert res.value('title') == toml.Any('TOML Example')
|
||||
assert (res.value('database') as map[string]toml.Any)['server'] ? == toml.Any('192.168.1.1')
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ fn test_dates() {
|
|||
lt1 = 07:32:00
|
||||
lt2 = 00:32:00.999999
|
||||
'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
// Re-use vars
|
||||
mut odt_time := toml.DateTime{'1979-05-27T07:32:00Z'}
|
||||
|
|
|
@ -3,7 +3,7 @@ import toml
|
|||
fn test_default_to() {
|
||||
default_value := 4321
|
||||
mut toml_txt := 'var = 1234'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
value := toml_doc.value('tar').default_to(default_value).int()
|
||||
assert value == default_value
|
||||
}
|
||||
|
|
|
@ -2,17 +2,14 @@ import os
|
|||
import toml
|
||||
import toml.to
|
||||
|
||||
fn test_parse() {
|
||||
toml_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.toml'
|
||||
toml_doc := toml.parse(toml_file) or { panic(err) }
|
||||
const fprefix = os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))
|
||||
|
||||
fn test_parse() ? {
|
||||
toml_doc := toml.parse_file(os.real_path(fprefix + '.toml')) ?
|
||||
|
||||
toml_json := to.json(toml_doc)
|
||||
out_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.out'
|
||||
out_file_json := os.read_file(out_file) or { panic(err) }
|
||||
println(toml_json)
|
||||
|
||||
out_file_json := os.read_file(os.real_path(fprefix + '.out')) ?
|
||||
assert toml_json == out_file_json
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ fn test_parse() {
|
|||
toml_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.toml'
|
||||
toml_doc := toml.parse(toml_file) or { panic(err) }
|
||||
toml_doc := toml.parse_file(toml_file) or { panic(err) }
|
||||
|
||||
toml_json := to.json(toml_doc)
|
||||
out_file :=
|
||||
|
|
|
@ -6,7 +6,7 @@ fn test_keys() {
|
|||
toml_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.toml'
|
||||
toml_doc := toml.parse(toml_file) or { panic(err) }
|
||||
toml_doc := toml.parse_file(toml_file) or { panic(err) }
|
||||
|
||||
mut value := toml_doc.value('34-11')
|
||||
assert value.int() == 23
|
||||
|
|
|
@ -15,7 +15,7 @@ fn test_large_file() {
|
|||
'.toml'
|
||||
if os.exists(toml_file) {
|
||||
println('Testing parsing of large (${os.file_size(toml_file)} bytes) "$toml_file"...')
|
||||
toml_doc := toml.parse(toml_file) or { panic(err) }
|
||||
toml_doc := toml.parse_file(toml_file) or { panic(err) }
|
||||
println('OK [1/1] "$toml_file"...') // So it can be checked with `v -stats test ...`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ enabled = true
|
|||
'
|
||||
|
||||
fn test_parse() {
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
// dump(toml_doc.ast)
|
||||
// assert false
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import toml
|
|||
fn test_quoted_keys() {
|
||||
str_value := 'V rocks!'
|
||||
toml_txt := 'a."b.c" = "V rocks!"'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value := toml_doc.value('a."b.c"')
|
||||
assert value == toml.Any(str_value)
|
||||
|
|
|
@ -50,7 +50,7 @@ mut:
|
|||
}
|
||||
|
||||
fn test_reflect() {
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
|
||||
mut user := toml_doc.reflect<User>()
|
||||
user.bio = toml_doc.value('bio').reflect<Bio>()
|
||||
|
|
|
@ -12,7 +12,7 @@ fn test_spaced_keys() {
|
|||
[ tube . test . "test.test" ]
|
||||
h . "i.j." . "k" = "Cryptic"
|
||||
'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
mut value := toml_doc.value('a."b.c"[0].d.e')
|
||||
assert value == toml.Any(str_value)
|
||||
assert value as string == str_value
|
||||
|
|
|
@ -33,7 +33,7 @@ long = "\U000003B4"'
|
|||
)
|
||||
|
||||
fn test_multiline_strings() {
|
||||
mut toml_doc := toml.parse(toml_multiline_text_1) or { panic(err) }
|
||||
mut toml_doc := toml.parse_text(toml_multiline_text_1) or { panic(err) }
|
||||
|
||||
mut value := toml_doc.value('multi1')
|
||||
assert value.string() == 'one'
|
||||
|
@ -44,7 +44,7 @@ fn test_multiline_strings() {
|
|||
value = toml_doc.value('multi4')
|
||||
assert value.string() == 'one\ntwo\nthree\nfour\n'
|
||||
|
||||
toml_doc = toml.parse(toml_multiline_text_2) or { panic(err) }
|
||||
toml_doc = toml.parse_text(toml_multiline_text_2) or { panic(err) }
|
||||
value = toml_doc.value('multi1')
|
||||
assert value.string() == 'one'
|
||||
value = toml_doc.value('multi2')
|
||||
|
@ -57,7 +57,7 @@ fn test_multiline_strings() {
|
|||
toml_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.toml'
|
||||
toml_doc = toml.parse(toml_file) or { panic(err) }
|
||||
toml_doc = toml.parse_file(toml_file) or { panic(err) }
|
||||
value = toml_doc.value('lit_one')
|
||||
assert value.string() == "'one quote'"
|
||||
value = toml_doc.value('lit_two')
|
||||
|
@ -69,7 +69,7 @@ fn test_multiline_strings() {
|
|||
}
|
||||
|
||||
fn test_unicode_escapes() {
|
||||
mut toml_doc := toml.parse(toml_unicode_escapes) or { panic(err) }
|
||||
mut toml_doc := toml.parse_text(toml_unicode_escapes) or { panic(err) }
|
||||
|
||||
mut value := toml_doc.value('short')
|
||||
assert value.string() == '\u03B4' // <- This escape is handled by V
|
||||
|
@ -81,7 +81,7 @@ fn test_literal_strings() {
|
|||
toml_file :=
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
'.toml'
|
||||
toml_doc := toml.parse(toml_file) or { panic(err) }
|
||||
toml_doc := toml.parse_file(toml_file) or { panic(err) }
|
||||
|
||||
assert toml_doc.value('lit1').string() == r'\' // '\'
|
||||
assert toml_doc.value('lit2').string() == r'\\' // '\\'
|
||||
|
|
|
@ -27,7 +27,7 @@ T = {a.b=2}'
|
|||
)
|
||||
|
||||
fn test_tables() {
|
||||
mut toml_doc := toml.parse(toml_table_text) or { panic(err) }
|
||||
mut toml_doc := toml.parse_text(toml_table_text) or { panic(err) }
|
||||
|
||||
mut value := toml_doc.value('inline.a.b')
|
||||
assert value.int() == 42
|
||||
|
|
|
@ -17,7 +17,7 @@ const (
|
|||
)
|
||||
|
||||
fn test_toml_with_bom() {
|
||||
toml_doc := toml.parse(toml_text_with_utf8_bom) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text_with_utf8_bom) or { panic(err) }
|
||||
toml_json := to.json(toml_doc)
|
||||
|
||||
title := toml_doc.value('title')
|
||||
|
@ -36,13 +36,13 @@ fn test_toml_with_bom() {
|
|||
|
||||
// Re-cycle bad_toml_doc
|
||||
mut bad_toml_doc := empty_toml_document
|
||||
bad_toml_doc = toml.parse(toml_text_with_utf16_bom) or {
|
||||
bad_toml_doc = toml.parse_text(toml_text_with_utf16_bom) or {
|
||||
println(' $err.msg()')
|
||||
assert true
|
||||
empty_toml_document
|
||||
}
|
||||
|
||||
bad_toml_doc = toml.parse(toml_text_with_utf32_bom) or {
|
||||
bad_toml_doc = toml.parse_text(toml_text_with_utf32_bom) or {
|
||||
println(' $err.msg()')
|
||||
assert true
|
||||
empty_toml_document
|
||||
|
|
|
@ -7,7 +7,7 @@ const toml_text = os.read_file(os.real_path(os.join_path(os.dir(@FILE), 'testdat
|
|||
'.toml') or { panic(err) }
|
||||
|
||||
fn test_toml_known_memory_corruption() {
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
|
||||
owner := toml_doc.value('owner') as map[string]toml.Any
|
||||
any_name := owner.value('name')
|
||||
|
@ -34,7 +34,7 @@ fn test_toml_known_memory_corruption_2() {
|
|||
lt1 = 07:32:00
|
||||
lt2 = 00:32:00.999999
|
||||
'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
// ldt1 test section
|
||||
odt_time := toml.DateTime{'1979-05-27T07:32:00'}
|
||||
|
|
|
@ -9,7 +9,7 @@ const toml_text = os.read_file(
|
|||
fn test_toml() {
|
||||
// File containing the complete text from the example in the official TOML project README.md:
|
||||
// https://github.com/toml-lang/toml/blob/3b11f6921da7b6f5db37af039aa021fee450c091/README.md#Example
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
toml_json := to.json(toml_doc)
|
||||
|
||||
// NOTE Kept for easier debugging:
|
||||
|
@ -98,7 +98,7 @@ fn test_toml_parse_text() {
|
|||
}
|
||||
|
||||
fn test_toml_parse() {
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
toml_json := to.json(toml_doc)
|
||||
assert toml_json == os.read_file(
|
||||
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
|
||||
|
|
|
@ -4,7 +4,7 @@ import strconv
|
|||
fn test_string() {
|
||||
str_value := 'test string'
|
||||
toml_txt := 'string = "test string"'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value := toml_doc.value('string')
|
||||
assert value == toml.Any(str_value)
|
||||
|
@ -14,7 +14,7 @@ fn test_string() {
|
|||
|
||||
fn test_i64() {
|
||||
toml_txt := 'i64 = 120'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value := toml_doc.value('i64')
|
||||
assert value == toml.Any(i64(120))
|
||||
|
@ -26,7 +26,7 @@ fn test_bool() {
|
|||
toml_txt := '
|
||||
bool_true = true
|
||||
bool_false = false'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value_true := toml_doc.value('bool_true')
|
||||
assert value_true == toml.Any(true)
|
||||
|
@ -46,7 +46,7 @@ bool_false = false'
|
|||
fn test_bool_key_is_not_value() {
|
||||
toml_txt := 'true = true
|
||||
false = false'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value_true := toml_doc.value('true')
|
||||
assert value_true == toml.Any(true)
|
||||
|
@ -64,7 +64,7 @@ false = false'
|
|||
fn test_single_letter_key() {
|
||||
toml_txt := '[v]
|
||||
open_sourced = "Jun 22 2019 20:20:28"'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value := toml_doc.value('v.open_sourced').string()
|
||||
assert value == 'Jun 22 2019 20:20:28'
|
||||
|
@ -74,7 +74,7 @@ fn test_hex_values() {
|
|||
// Regression test
|
||||
// '0xb' is carefully chosen to include the 'b' character that also denotes binary via 0b prefix.
|
||||
toml_txt := 'hex = 0xb'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value := toml_doc.value('hex')
|
||||
assert value as i64 == 11
|
||||
|
@ -85,7 +85,7 @@ fn test_comment_as_last_value() {
|
|||
toml_txt := '
|
||||
test = 42
|
||||
# this line has comment as last thing'
|
||||
toml_doc := toml.parse(toml_txt) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_txt) or { panic(err) }
|
||||
|
||||
value := toml_doc.value('test')
|
||||
assert value as i64 == 42
|
||||
|
@ -93,31 +93,31 @@ test = 42
|
|||
}
|
||||
|
||||
fn test_nan_and_inf_values() {
|
||||
mut toml_doc := toml.parse('nan = nan') or { panic(err) }
|
||||
mut toml_doc := toml.parse_text('nan = nan') or { panic(err) }
|
||||
mut value := toml_doc.value('nan')
|
||||
assert value.string() == 'nan'
|
||||
|
||||
toml_doc = toml.parse('nan = nan#comment') or { panic(err) }
|
||||
toml_doc = toml.parse_text('nan = nan#comment') or { panic(err) }
|
||||
value = toml_doc.value('nan')
|
||||
assert value.string() == 'nan'
|
||||
|
||||
toml_doc = toml.parse('nan = -nan') or { panic(err) }
|
||||
toml_doc = toml.parse_text('nan = -nan') or { panic(err) }
|
||||
value = toml_doc.value('nan')
|
||||
assert value.string() == 'nan'
|
||||
|
||||
toml_doc = toml.parse('nan = +nan') or { panic(err) }
|
||||
toml_doc = toml.parse_text('nan = +nan') or { panic(err) }
|
||||
value = toml_doc.value('nan')
|
||||
assert value.string() == 'nan'
|
||||
|
||||
toml_doc = toml.parse('inf = inf') or { panic(err) }
|
||||
toml_doc = toml.parse_text('inf = inf') or { panic(err) }
|
||||
value = toml_doc.value('inf')
|
||||
assert value.u64() == strconv.double_plus_infinity
|
||||
|
||||
toml_doc = toml.parse('inf = +inf') or { panic(err) }
|
||||
toml_doc = toml.parse_text('inf = +inf') or { panic(err) }
|
||||
value = toml_doc.value('inf')
|
||||
assert value.u64() == strconv.double_plus_infinity
|
||||
|
||||
toml_doc = toml.parse('inf = -inf') or { panic(err) }
|
||||
toml_doc = toml.parse_text('inf = -inf') or { panic(err) }
|
||||
value = toml_doc.value('inf')
|
||||
assert value.u64() == strconv.double_minus_infinity
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ colors = [
|
|||
)
|
||||
|
||||
fn test_value_query_in_array() {
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
mut value := toml_doc.value('themes[0].colors[1]').string()
|
||||
assert value == 'black'
|
||||
value = toml_doc.value('themes[1].colors[0]').string()
|
||||
|
@ -67,7 +67,7 @@ fn test_value_query_in_array() {
|
|||
}
|
||||
|
||||
fn test_any_value_query() {
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
themes := toml_doc.value('themes')
|
||||
assert themes.value('[0].colors[0]').string() == 'red'
|
||||
|
||||
|
@ -94,7 +94,7 @@ fn test_any_value_query() {
|
|||
}
|
||||
|
||||
fn test_inf_and_nan_query() {
|
||||
toml_doc := toml.parse(toml_text) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text) or { panic(err) }
|
||||
|
||||
value := toml_doc.value('values.nan').string()
|
||||
assert value == 'nan'
|
||||
|
@ -106,7 +106,7 @@ fn test_inf_and_nan_query() {
|
|||
}
|
||||
|
||||
fn test_any_value_query_2() {
|
||||
toml_doc := toml.parse(toml_text_2) or { panic(err) }
|
||||
toml_doc := toml.parse_text(toml_text_2) or { panic(err) }
|
||||
defaults := toml_doc.value('defaults')
|
||||
assert defaults.value('run.flags[0]').string() == '-f 1'
|
||||
assert defaults.value('env[0].RUN_TIME').int() == 5
|
||||
|
|
|
@ -108,6 +108,8 @@ pub fn parse_text(text string) ?Doc {
|
|||
// parse parses the TOML document provided in `toml`.
|
||||
// parse automatically try to determine if the type of `toml` is a file or text.
|
||||
// For explicit parsing of input types see `parse_file` or `parse_text`.
|
||||
[deprecated: 'use parse_file or parse_text instead']
|
||||
[deprecated_after: '2022-06-18']
|
||||
pub fn parse(toml string) ?Doc {
|
||||
mut input_config := input.auto_config(toml) ?
|
||||
scanner_config := scanner.Config{
|
||||
|
|
|
@ -557,6 +557,7 @@ pub mut:
|
|||
name string // left.name()
|
||||
is_method bool
|
||||
is_field bool // temp hack, remove ASAP when re-impl CallExpr / Selector (joe)
|
||||
is_fn_var bool // fn variable
|
||||
is_keep_alive bool // GC must not free arguments before fn returns
|
||||
is_noreturn bool // whether the function/method is marked as [noreturn]
|
||||
is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V
|
||||
|
@ -568,6 +569,7 @@ pub mut:
|
|||
left_type Type // type of `user`
|
||||
receiver_type Type // User
|
||||
return_type Type
|
||||
fn_var_type Type // fn variable type
|
||||
should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
|
||||
concrete_types []Type // concrete types, e.g. <int, string>
|
||||
concrete_list_pos token.Pos
|
||||
|
@ -1692,10 +1694,10 @@ pub:
|
|||
|
||||
[inline]
|
||||
pub fn (expr Expr) is_blank_ident() bool {
|
||||
match expr {
|
||||
Ident { return expr.kind == .blank_ident }
|
||||
else { return false }
|
||||
if expr is Ident {
|
||||
return expr.kind == .blank_ident
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (expr Expr) pos() token.Pos {
|
||||
|
|
|
@ -257,10 +257,6 @@ fn (ts TypeSymbol) dbg_common(mut res []string) {
|
|||
res << 'language: $ts.language'
|
||||
}
|
||||
|
||||
pub fn (t Type) str() string {
|
||||
return 'ast.Type(0x$t.hex() = ${u32(t)})'
|
||||
}
|
||||
|
||||
pub fn (t &Table) type_str(typ Type) string {
|
||||
sym := t.sym(typ)
|
||||
return sym.name
|
||||
|
@ -980,6 +976,12 @@ pub fn (mytable &Table) type_to_code(t Type) string {
|
|||
}
|
||||
}
|
||||
|
||||
// clean type name from generics form. From Type<int> -> Type
|
||||
pub fn (t &Table) clean_generics_type_str(typ Type) string {
|
||||
result := t.type_to_str(typ)
|
||||
return result.all_before('<')
|
||||
}
|
||||
|
||||
// import_aliases is a map of imported symbol aliases 'module.Type' => 'Type'
|
||||
pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]string) string {
|
||||
sym := t.sym(typ)
|
||||
|
@ -1186,7 +1188,7 @@ pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string
|
|||
// TODO write receiver
|
||||
}
|
||||
if !opts.type_only {
|
||||
sb.write_string('$func.name')
|
||||
sb.write_string(func.name)
|
||||
}
|
||||
sb.write_string('(')
|
||||
start := int(opts.skip_receiver)
|
||||
|
@ -1203,22 +1205,38 @@ pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string
|
|||
sb.write_string('mut ')
|
||||
}
|
||||
if !opts.type_only {
|
||||
sb.write_string('$param.name ')
|
||||
sb.write_string(param.name)
|
||||
sb.write_string(' ')
|
||||
}
|
||||
styp := t.type_to_str_using_aliases(typ, import_aliases)
|
||||
if i == func.params.len - 1 && func.is_variadic {
|
||||
sb.write_string('...$styp')
|
||||
sb.write_string('...')
|
||||
sb.write_string(styp)
|
||||
} else {
|
||||
sb.write_string('$styp')
|
||||
sb.write_string(styp)
|
||||
}
|
||||
}
|
||||
sb.write_string(')')
|
||||
if func.return_type != ast.void_type {
|
||||
sb.write_string(' ${t.type_to_str_using_aliases(func.return_type, import_aliases)}')
|
||||
sb.write_string(' ')
|
||||
sb.write_string(t.type_to_str_using_aliases(func.return_type, import_aliases))
|
||||
}
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
// Get the name of the complete quanlified name of the type
|
||||
// without the generic parts.
|
||||
pub fn (t &TypeSymbol) symbol_name_except_generic() string {
|
||||
// main.Abc<int>
|
||||
mut embed_name := t.name
|
||||
// remove generic part from name
|
||||
// main.Abc<int> => main.Abc
|
||||
if embed_name.contains('<') {
|
||||
embed_name = embed_name.all_before('<')
|
||||
}
|
||||
return embed_name
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) embed_name() string {
|
||||
// main.Abc<int> => Abc<int>
|
||||
mut embed_name := t.name.split('.').last()
|
||||
|
|
|
@ -188,7 +188,39 @@ pub fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type,
|
|||
return
|
||||
}
|
||||
}
|
||||
return error('cannot use `${c.table.type_to_str(got.clear_flag(.variadic))}` as `${c.table.type_to_str(expected.clear_flag(.variadic))}`')
|
||||
|
||||
// Check on Generics types, there are some case where we have the following case
|
||||
// `&Type<int> == &Type<>`. This is a common case we are implementing a function
|
||||
// with generic parameters like `compare(bst Bst<T> node) {}`
|
||||
got_typ_sym := c.table.sym(got)
|
||||
got_typ_str := c.table.type_to_str(got.clear_flag(.variadic))
|
||||
expected_typ_sym := c.table.sym(expected_)
|
||||
expected_typ_str := c.table.type_to_str(expected.clear_flag(.variadic))
|
||||
|
||||
if got_typ_sym.symbol_name_except_generic() == expected_typ_sym.symbol_name_except_generic() {
|
||||
// Check if we are making a comparison between two different types of
|
||||
// the same type like `Type<int> and &Type<>`
|
||||
if (got.is_ptr() != expected.is_ptr()) || !c.check_same_module(got, expected) {
|
||||
return error('cannot use `$got_typ_str` as `$expected_typ_str`')
|
||||
}
|
||||
return
|
||||
}
|
||||
return error('cannot use `$got_typ_str` as `$expected_typ_str`')
|
||||
}
|
||||
|
||||
// helper method to check if the type is of the same module.
|
||||
// FIXME(vincenzopalazzo) This is a work around to the issue
|
||||
// explained in the https://github.com/vlang/v/pull/13718#issuecomment-1074517800
|
||||
fn (c Checker) check_same_module(got ast.Type, expected ast.Type) bool {
|
||||
clean_got_typ := c.table.clean_generics_type_str(got.clear_flag(.variadic)).all_before('<')
|
||||
clean_expected_typ := c.table.clean_generics_type_str(expected.clear_flag(.variadic)).all_before('<')
|
||||
if clean_got_typ == clean_expected_typ {
|
||||
return true
|
||||
// The following if confition should catch the bugs descripted in the issue
|
||||
} else if clean_expected_typ.all_after('.') == clean_got_typ.all_after('.') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) check_basic(got ast.Type, expected ast.Type) bool {
|
||||
|
@ -580,7 +612,12 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Typ
|
|||
}
|
||||
c.fail_if_unreadable(expr, ftyp, 'interpolation object')
|
||||
node.expr_types << ftyp
|
||||
typ := c.table.unalias_num_type(ftyp)
|
||||
ftyp_sym := c.table.sym(ftyp)
|
||||
typ := if ftyp_sym.kind == .alias && !ftyp_sym.has_method('str') {
|
||||
c.table.unalias_num_type(ftyp)
|
||||
} else {
|
||||
ftyp
|
||||
}
|
||||
mut fmt := node.fmts[i]
|
||||
// analyze and validate format specifier
|
||||
if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `S`, `p`,
|
||||
|
|
|
@ -91,9 +91,10 @@ pub mut:
|
|||
inside_fn_arg bool // `a`, `b` in `a.f(b)`
|
||||
inside_ct_attr bool // true inside `[if expr]`
|
||||
inside_comptime_for_field bool
|
||||
skip_flags bool // should `#flag` and `#include` be skipped
|
||||
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
|
||||
smartcast_mut_pos token.Pos
|
||||
skip_flags bool // should `#flag` and `#include` be skipped
|
||||
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
|
||||
smartcast_mut_pos token.Pos // match mut foo, if mut foo is Foo
|
||||
smartcast_cond_pos token.Pos // match cond
|
||||
ct_cond_stack []ast.Expr
|
||||
mut:
|
||||
stmt_level int // the nesting level inside each stmts list;
|
||||
|
@ -594,12 +595,20 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
|||
match mut node.left {
|
||||
ast.Ident, ast.SelectorExpr {
|
||||
if node.left.is_mut {
|
||||
c.error('remove unnecessary `mut`', node.left.mut_pos)
|
||||
c.error('the `mut` keyword is invalid here', node.left.mut_pos)
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
match mut node.right {
|
||||
ast.Ident, ast.SelectorExpr {
|
||||
if node.right.is_mut {
|
||||
c.error('the `mut` keyword is invalid here', node.right.mut_pos)
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
eq_ne := node.op in [.eq, .ne]
|
||||
// Single side check
|
||||
// Place these branches according to ops' usage frequency to accelerate.
|
||||
|
@ -1522,41 +1531,36 @@ fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_ret
|
|||
stmt.pos)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match stmt {
|
||||
ast.ExprStmt {
|
||||
match stmt.expr {
|
||||
ast.IfExpr {
|
||||
for branch in stmt.expr.branches {
|
||||
last_stmt := branch.stmts[branch.stmts.len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
}
|
||||
ast.MatchExpr {
|
||||
for branch in stmt.expr.branches {
|
||||
last_stmt := branch.stmts[branch.stmts.len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if stmt.typ == ast.void_type {
|
||||
return
|
||||
}
|
||||
if is_noreturn_callexpr(stmt.expr) {
|
||||
return
|
||||
}
|
||||
if c.check_types(stmt.typ, expr_return_type) {
|
||||
return
|
||||
}
|
||||
// opt_returning_string() or { ... 123 }
|
||||
type_name := c.table.type_to_str(stmt.typ)
|
||||
expr_return_type_name := c.table.type_to_str(expr_return_type)
|
||||
c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`',
|
||||
stmt.expr.pos())
|
||||
}
|
||||
} else if stmt is ast.ExprStmt {
|
||||
match stmt.expr {
|
||||
ast.IfExpr {
|
||||
for branch in stmt.expr.branches {
|
||||
last_stmt := branch.stmts[branch.stmts.len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
}
|
||||
else {}
|
||||
ast.MatchExpr {
|
||||
for branch in stmt.expr.branches {
|
||||
last_stmt := branch.stmts[branch.stmts.len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if stmt.typ == ast.void_type {
|
||||
return
|
||||
}
|
||||
if is_noreturn_callexpr(stmt.expr) {
|
||||
return
|
||||
}
|
||||
if c.check_types(stmt.typ, expr_return_type) {
|
||||
return
|
||||
}
|
||||
// opt_returning_string() or { ... 123 }
|
||||
type_name := c.table.type_to_str(stmt.typ)
|
||||
expr_return_type_name := c.table.type_to_str(expr_return_type)
|
||||
c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`',
|
||||
stmt.expr.pos())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1761,6 +1765,10 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
|
|||
c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value',
|
||||
c.smartcast_mut_pos)
|
||||
}
|
||||
if c.smartcast_cond_pos != token.Pos{} {
|
||||
c.note('smartcast can only be used on the ident or selector, e.g. match foo, match foo.bar',
|
||||
c.smartcast_cond_pos)
|
||||
}
|
||||
c.error(unknown_field_msg, node.pos)
|
||||
}
|
||||
return ast.void_type
|
||||
|
@ -2089,6 +2097,10 @@ fn (mut c Checker) global_decl(mut node ast.GlobalDecl) {
|
|||
c.error('unknown type `$sym.name`', field.typ_pos)
|
||||
}
|
||||
if field.has_expr {
|
||||
if field.expr is ast.AnonFn && field.name == 'main' {
|
||||
c.error('the `main` function is the program entry point, cannot redefine it',
|
||||
field.pos)
|
||||
}
|
||||
field.typ = c.expr(field.expr)
|
||||
mut v := c.file.global_scope.find_global(field.name) or {
|
||||
panic('internal compiler error - could not find global in scope')
|
||||
|
@ -3349,7 +3361,9 @@ fn (mut c Checker) smartcast(expr_ ast.Expr, cur_type ast.Type, to_type_ ast.Typ
|
|||
c.smartcast_mut_pos = expr.pos
|
||||
}
|
||||
}
|
||||
else {}
|
||||
else {
|
||||
c.smartcast_cond_pos = expr.pos()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3765,6 +3779,9 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
|
|||
&& typ !in [ast.byteptr_type, ast.charptr_type] && !typ.has_flag(.variadic) {
|
||||
c.error('type `$typ_sym.name` does not support indexing', node.pos)
|
||||
}
|
||||
if typ.has_flag(.optional) {
|
||||
c.error('type `?$typ_sym.name` is optional, it does not support indexing', node.left.pos())
|
||||
}
|
||||
if typ_sym.kind == .string && !typ.is_ptr() && node.is_setter {
|
||||
c.error('cannot assign to s[i] since V strings are immutable\n' +
|
||||
'(note, that variables may be mutable but string values are always immutable, like in Go and Java)',
|
||||
|
|
|
@ -352,32 +352,12 @@ fn (mut c Checker) anon_fn(mut node ast.AnonFn) ast.Type {
|
|||
}
|
||||
|
||||
pub fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
|
||||
// First check everything that applies to both fns and methods
|
||||
// TODO merge logic from method_call and fn_call
|
||||
/*
|
||||
for i, call_arg in node.args {
|
||||
if call_arg.is_mut {
|
||||
c.fail_if_immutable(call_arg.expr)
|
||||
if !arg.is_mut {
|
||||
tok := call_arg.share.str()
|
||||
c.error('`$node.name` parameter `$arg.name` is not `$tok`, `$tok` is not needed`',
|
||||
call_arg.expr.pos())
|
||||
} else if arg.typ.share() != call_arg.share {
|
||||
c.error('wrong shared type', call_arg.expr.pos())
|
||||
}
|
||||
} else {
|
||||
if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) {
|
||||
tok := call_arg.share.str()
|
||||
c.error('`$node.name` parameter `$arg.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i+1}`',
|
||||
call_arg.expr.pos())
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Now call `method_call` or `fn_call` for specific checks.
|
||||
// First check everything that applies to both fns and methods
|
||||
old_inside_fn_arg := c.inside_fn_arg
|
||||
c.inside_fn_arg = true
|
||||
mut continue_check := true
|
||||
// Now call `method_call` or `fn_call` for specific checks.
|
||||
typ := if node.is_method {
|
||||
c.method_call(mut node)
|
||||
} else {
|
||||
|
@ -620,9 +600,13 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||
match obj {
|
||||
ast.GlobalField {
|
||||
typ = obj.typ
|
||||
node.is_fn_var = true
|
||||
node.fn_var_type = typ
|
||||
}
|
||||
ast.Var {
|
||||
typ = if obj.smartcasts.len != 0 { obj.smartcasts.last() } else { obj.typ }
|
||||
node.is_fn_var = true
|
||||
node.fn_var_type = typ
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
@ -810,7 +794,8 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||
call_arg.expr.pos())
|
||||
} else {
|
||||
if param.typ.share() != call_arg.share {
|
||||
c.error('wrong shared type', call_arg.expr.pos())
|
||||
c.error('wrong shared type `$call_arg.share.str()`, expected: `$param.typ.share().str()`',
|
||||
call_arg.expr.pos())
|
||||
}
|
||||
if to_lock != '' && !param.typ.has_flag(.shared_f) {
|
||||
c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`',
|
||||
|
@ -1258,10 +1243,6 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||
final_arg_sym = c.table.sym(final_arg_typ)
|
||||
}
|
||||
if exp_arg_typ.has_flag(.generic) {
|
||||
if concrete_types.len == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if exp_utyp := c.table.resolve_generic_to_concrete(exp_arg_typ, method.generic_names,
|
||||
concrete_types)
|
||||
{
|
||||
|
@ -1299,7 +1280,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||
arg.expr.pos())
|
||||
} else {
|
||||
if param_share != arg.share {
|
||||
c.error('wrong shared type', arg.expr.pos())
|
||||
c.error('wrong shared type `$arg.share.str()`, expected: `$param_share.str()`',
|
||||
arg.expr.pos())
|
||||
}
|
||||
if to_lock != '' && param_share != .shared_t {
|
||||
c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`',
|
||||
|
|
|
@ -152,9 +152,8 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
} else {
|
||||
c.stmts(branch.stmts)
|
||||
}
|
||||
if c.smartcast_mut_pos != token.Pos{} {
|
||||
c.smartcast_mut_pos = token.Pos{}
|
||||
}
|
||||
c.smartcast_mut_pos = token.Pos{}
|
||||
c.smartcast_cond_pos = token.Pos{}
|
||||
}
|
||||
if expr_required {
|
||||
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
|
||||
|
|
|
@ -43,9 +43,8 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
|
|||
} else {
|
||||
c.stmts(branch.stmts)
|
||||
}
|
||||
if c.smartcast_mut_pos != token.Pos{} {
|
||||
c.smartcast_mut_pos = token.Pos{}
|
||||
}
|
||||
c.smartcast_mut_pos = token.Pos{}
|
||||
c.smartcast_cond_pos = token.Pos{}
|
||||
if node.is_expr {
|
||||
if branch.stmts.len > 0 {
|
||||
// ignore last statement - workaround
|
||||
|
@ -66,37 +65,36 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
|
|||
// If the last statement is an expression, return its type
|
||||
if branch.stmts.len > 0 {
|
||||
mut stmt := branch.stmts[branch.stmts.len - 1]
|
||||
match mut stmt {
|
||||
ast.ExprStmt {
|
||||
if node.is_expr {
|
||||
c.expected_type = node.expected_type
|
||||
if mut stmt is ast.ExprStmt {
|
||||
if node.is_expr {
|
||||
c.expected_type = node.expected_type
|
||||
}
|
||||
expr_type := c.expr(stmt.expr)
|
||||
if first_iteration {
|
||||
if node.is_expr && (node.expected_type.has_flag(.optional)
|
||||
|| c.table.type_kind(node.expected_type) == .sum_type) {
|
||||
ret_type = node.expected_type
|
||||
} else {
|
||||
ret_type = expr_type
|
||||
}
|
||||
expr_type := c.expr(stmt.expr)
|
||||
if first_iteration {
|
||||
if node.is_expr && (node.expected_type.has_flag(.optional)
|
||||
|| c.table.type_kind(node.expected_type) == .sum_type) {
|
||||
ret_type = node.expected_type
|
||||
} else {
|
||||
ret_type = expr_type
|
||||
}
|
||||
stmt.typ = expr_type
|
||||
} else if node.is_expr && ret_type.idx() != expr_type.idx() {
|
||||
if !c.check_types(ret_type, expr_type)
|
||||
&& !c.check_types(expr_type, ret_type) {
|
||||
ret_sym := c.table.sym(ret_type)
|
||||
is_noreturn := is_noreturn_callexpr(stmt.expr)
|
||||
if !(node.is_expr && ret_sym.kind == .sum_type) && !is_noreturn {
|
||||
c.error('return type mismatch, it should be `$ret_sym.name`',
|
||||
stmt.expr.pos())
|
||||
}
|
||||
stmt.typ = expr_type
|
||||
} else if node.is_expr && ret_type.idx() != expr_type.idx() {
|
||||
if !c.check_types(ret_type, expr_type) && !c.check_types(expr_type, ret_type) {
|
||||
ret_sym := c.table.sym(ret_type)
|
||||
is_noreturn := is_noreturn_callexpr(stmt.expr)
|
||||
if !(node.is_expr && ret_sym.kind == .sum_type
|
||||
&& (ret_type.has_flag(.generic)
|
||||
|| c.table.is_sumtype_or_in_variant(ret_type, expr_type)))
|
||||
&& !is_noreturn {
|
||||
c.error('return type mismatch, it should be `$ret_sym.name`',
|
||||
stmt.expr.pos())
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if node.is_expr && ret_type != ast.void_type {
|
||||
c.error('`match` expression requires an expression as the last statement of every branch',
|
||||
stmt.pos)
|
||||
}
|
||||
} else {
|
||||
if node.is_expr && ret_type != ast.void_type {
|
||||
c.error('`match` expression requires an expression as the last statement of every branch',
|
||||
stmt.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,10 +117,6 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
|
|||
c.returns = false
|
||||
}
|
||||
}
|
||||
// if ret_type != ast.void_type {
|
||||
// node.is_expr = c.expected_type != ast.void_type
|
||||
// node.expected_type = c.expected_type
|
||||
// }
|
||||
node.return_type = ret_type
|
||||
cond_var := c.get_base_name(&node.cond)
|
||||
if cond_var != '' {
|
||||
|
@ -241,7 +235,11 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
|
|||
if expr_type !in cond_type_sym.info.variants {
|
||||
expr_str := c.table.type_to_str(expr_type)
|
||||
expect_str := c.table.type_to_str(node.cond_type)
|
||||
c.error('`$expect_str` has no variant `$expr_str`', expr.pos())
|
||||
sumtype_variant_names := cond_type_sym.info.variants.map(c.table.type_to_str_using_aliases(it,
|
||||
{}))
|
||||
suggestion := util.new_suggestion(expr_str, sumtype_variant_names)
|
||||
c.error(suggestion.say('`$expect_str` has no variant `$expr_str`'),
|
||||
expr.pos())
|
||||
}
|
||||
} else if cond_type_sym.info is ast.Alias && expr_type_sym.info is ast.Struct {
|
||||
expr_str := c.table.type_to_str(expr_type)
|
||||
|
|
|
@ -55,6 +55,16 @@ pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
|||
field.type_pos)
|
||||
}
|
||||
}
|
||||
field_sym := c.table.sym(field.typ)
|
||||
if field_sym.kind == .function {
|
||||
fn_info := field_sym.info as ast.FnType
|
||||
c.ensure_type_exists(fn_info.func.return_type, fn_info.func.return_type_pos) or {
|
||||
return
|
||||
}
|
||||
for param in fn_info.func.params {
|
||||
c.ensure_type_exists(param.typ, param.type_pos) or { return }
|
||||
}
|
||||
}
|
||||
}
|
||||
if sym.kind == .struct_ {
|
||||
info := sym.info as ast.Struct
|
||||
|
@ -324,6 +334,15 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
|
|||
field.pos)
|
||||
}
|
||||
}
|
||||
if field_type_sym.kind == .function && field_type_sym.language == .v {
|
||||
pos := field.expr.pos()
|
||||
if mut field.expr is ast.AnonFn {
|
||||
if field.expr.decl.no_body {
|
||||
c.error('cannot initialize the fn field with anonymous fn that does not have a body',
|
||||
pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
node.fields[i].typ = expr_type
|
||||
node.fields[i].expected_type = field_info.typ
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
module amod
|
||||
|
||||
pub struct Xyz {}
|
||||
|
||||
pub struct Bcg {}
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/generic_parameter_on_method.vv:15:15: error: cannot use `&Type<int>` as `Type<>` in argument 1 to `ContainerType<int>.contains`
|
||||
13 | fn main() {
|
||||
14 | con := ContainerType<int>{typ: &Type<int>{0}}
|
||||
15 | con.contains(con.typ)
|
||||
| ~~~~~~~
|
||||
16 | println(con)
|
||||
17 | }
|
|
@ -0,0 +1,17 @@
|
|||
struct Type<T> {
|
||||
value T
|
||||
}
|
||||
|
||||
struct ContainerType<T> {
|
||||
typ &Type<T>
|
||||
}
|
||||
|
||||
fn (instance &ContainerType<T>) contains(typ Type<T>) {
|
||||
println(typ)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
con := ContainerType<int>{typ: &Type<int>{0}}
|
||||
con.contains(con.typ)
|
||||
println(con)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
vlib/v/checker/tests/globals/redefine_main.vv:1:10: error: the `main` function is the program entry point, cannot redefine it
|
||||
1 | __global main = fn () int { return 22 }
|
||||
| ~~~~
|
|
@ -0,0 +1 @@
|
|||
__global main = fn () int { return 22 }
|
|
@ -0,0 +1,14 @@
|
|||
vlib/v/checker/tests/incorrect_smartcast2_err.vv:24:9: notice: smartcast can only be used on the ident or selector, e.g. match foo, match foo.bar
|
||||
22 |
|
||||
23 | fn doesntwork(v []Either<int, int>) {
|
||||
24 | match v[0] {
|
||||
| ~~~
|
||||
25 | Left<int> {
|
||||
26 | println(v[0].error)
|
||||
vlib/v/checker/tests/incorrect_smartcast2_err.vv:26:17: error: field `error` does not exist or have the same type in all sumtype variants
|
||||
24 | match v[0] {
|
||||
25 | Left<int> {
|
||||
26 | println(v[0].error)
|
||||
| ~~~~~
|
||||
27 | }
|
||||
28 | else {}
|
|
@ -0,0 +1,32 @@
|
|||
struct Left<E> {
|
||||
error E
|
||||
}
|
||||
|
||||
struct Right<T> {
|
||||
inner T
|
||||
}
|
||||
|
||||
type Either<T, E> =
|
||||
Left<E> |
|
||||
Right<T>
|
||||
|
||||
fn works(v []Either<int, int>) {
|
||||
first := v[0]
|
||||
match first {
|
||||
Left<int> {
|
||||
println(first.error)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
fn doesntwork(v []Either<int, int>) {
|
||||
match v[0] {
|
||||
Left<int> {
|
||||
println(v[0].error)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/index_of_optional_err.vv:6:7: error: type `?[]int` is optional, it does not support indexing
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | a := abc()[0] or { 5 }
|
||||
| ~~~~~
|
||||
7 | dump(a)
|
||||
8 | }
|
|
@ -0,0 +1,8 @@
|
|||
fn abc() ?[]int {
|
||||
return [1, 2, 3]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a := abc()[0] or { 5 }
|
||||
dump(a)
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
vlib/v/checker/tests/invalid_mut.vv:3:5: error: the `mut` keyword is invalid here
|
||||
1 | fn main() {
|
||||
2 | mut x := 0
|
||||
3 | if mut x == 0 {
|
||||
| ~~~
|
||||
4 | println(true)
|
||||
5 | }
|
||||
vlib/v/checker/tests/invalid_mut.vv:6:10: error: the `mut` keyword is invalid here
|
||||
4 | println(true)
|
||||
5 | }
|
||||
6 | if 0 == mut x {
|
||||
| ~~~
|
||||
7 | println(true)
|
||||
8 | }
|
|
@ -3,5 +3,8 @@ fn main() {
|
|||
if mut x == 0 {
|
||||
println(true)
|
||||
}
|
||||
if 0 == mut x {
|
||||
println(true)
|
||||
}
|
||||
_ = x
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
vlib/v/checker/tests/match_invalid_type.vv:5:3: error: `IoS` has no variant `byte`
|
||||
vlib/v/checker/tests/match_invalid_type.vv:5:3: error: `IoS` has no variant `byte`.
|
||||
2 possibilities: `int`, `string`.
|
||||
3 | fn sum() {
|
||||
4 | match IoS(1) {
|
||||
5 | byte {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/match_return_sumtype_mismatch_err.vv:15:11: error: return type mismatch, it should be `Myt`
|
||||
13 | return match b {
|
||||
14 | true { St('TRUE') }
|
||||
15 | false { `F` }
|
||||
| ~~~
|
||||
16 | }
|
||||
17 | }
|
|
@ -0,0 +1,19 @@
|
|||
type St = string
|
||||
type Ru = rune
|
||||
type Myt = Ru | St
|
||||
|
||||
fn myt_t1(b bool) Myt {
|
||||
match b {
|
||||
true { return St('TRUE') }
|
||||
false { return Ru(`F`) }
|
||||
}
|
||||
}
|
||||
|
||||
fn myt_t2(b bool) Myt {
|
||||
return match b {
|
||||
true { St('TRUE') }
|
||||
false { `F` }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/struct_field_init_with_nobody_anon_fn_err.vv:7:7: error: cannot initialize the fn field with anonymous fn that does not have a body
|
||||
5 | fn main() {
|
||||
6 | _ = App{
|
||||
7 | cb: fn(x int) // Note the missing `{}` (the function body) here
|
||||
| ~~~~~~~~~
|
||||
8 | }
|
||||
9 | }
|
|
@ -0,0 +1,9 @@
|
|||
struct App {
|
||||
cb fn(x int) // the function signature doesn't make a difference
|
||||
}
|
||||
|
||||
fn main() {
|
||||
_ = App{
|
||||
cb: fn(x int) // Note the missing `{}` (the function body) here
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
vlib/v/checker/tests/sumtype_has_no_variant_suggestion.vv:14:5: error: `Abc` has no variant `amod.NonExisting`.
|
||||
5 possibilities: `amod.Bcg`, `amod.Xyz`, `AnotherStruct`, `Struct1`, `ThirdStruct`.
|
||||
12 | a := Abc(Struct1{})
|
||||
13 | match a {
|
||||
14 | x.NonExisting { println('----') }
|
||||
| ~~~~~~~~~~~
|
||||
15 | else {}
|
||||
16 | }
|
|
@ -0,0 +1,17 @@
|
|||
import v.checker.tests.amod as x
|
||||
|
||||
struct Struct1 {}
|
||||
|
||||
struct AnotherStruct {}
|
||||
|
||||
struct ThirdStruct {}
|
||||
|
||||
type Abc = AnotherStruct | Struct1 | ThirdStruct | x.Bcg | x.Xyz
|
||||
|
||||
fn main() {
|
||||
a := Abc(Struct1{})
|
||||
match a {
|
||||
x.NonExisting { println('----') }
|
||||
else {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/unknown_type_in_anon_fn.vv:5:10: error: unknown type `Another`
|
||||
3 | struct Struc{
|
||||
4 | mut:
|
||||
5 | f fn (s Another, i int) ?
|
||||
| ~~~~~~~
|
||||
6 | }
|
||||
7 |
|
|
@ -0,0 +1,8 @@
|
|||
module main
|
||||
|
||||
struct Struc{
|
||||
mut:
|
||||
f fn (s Another, i int) ?
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,35 @@
|
|||
vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:29:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
|
||||
27 | // ritorna una nuova Quadrica somma del ricevente e di un'altra
|
||||
28 | fn (q &Quad) add(other &Quad) &Quad {
|
||||
29 | return &(Quad{q.x + other.x, q.y + other.y, q.z + other.z, q.w + other.w})
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
30 | }
|
||||
31 |
|
||||
vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:34:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
|
||||
32 | // ritorna una nuova Quadrica differenza tra il ricevente e un'altra
|
||||
33 | fn (q &Quad) sub(other &Quad) &Quad {
|
||||
34 | return &(Quad{q.x - other.x, q.y - other.y, q.z - other.z, q.w - other.w})
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
35 | }
|
||||
36 |
|
||||
vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:39:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
|
||||
37 | // ritorna una nuova Quadrica ottenuta negando il ricevente
|
||||
38 | fn (q &Quad) neg() &Quad {
|
||||
39 | return &(Quad{-q.x, -q.y, -q.z, -q.w})
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
40 | }
|
||||
41 |
|
||||
vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:44:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
|
||||
42 | // ritorna una nuova Quadrica ottenuta moltiplicando il ricevente per una costante
|
||||
43 | fn (q &Quad) mult(factor f64) &Quad {
|
||||
44 | return &(Quad{q.x * factor, q.y * factor, q.z * factor, q.w * factor})
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
45 | }
|
||||
46 |
|
||||
vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:49:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
|
||||
47 | // ritorna una nuova Quadrica ottenuta dividendo il ricevente per una costante
|
||||
48 | fn (q &Quad) div(factor f64) &Quad {
|
||||
49 | return &(Quad{q.x / factor, q.y / factor, q.z / factor, q.w / factor})
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
50 | }
|
||||
51 |
|
|
@ -0,0 +1,69 @@
|
|||
struct Quad {
|
||||
mut:
|
||||
x f64
|
||||
y f64
|
||||
z f64
|
||||
w f64
|
||||
}
|
||||
|
||||
fn (q Quad) get(i int) f64 {
|
||||
return match i {
|
||||
0 { q.x }
|
||||
1 { q.y }
|
||||
2 { q.z }
|
||||
else { q.w }
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut q Quad) set(i int, v f64) {
|
||||
match i {
|
||||
0 { q.x = v }
|
||||
1 { q.y = v }
|
||||
2 { q.z = v }
|
||||
else { q.w = v }
|
||||
}
|
||||
}
|
||||
|
||||
// ritorna una nuova Quadrica somma del ricevente e di un'altra
|
||||
fn (q &Quad) add(other &Quad) &Quad {
|
||||
return &(Quad{q.x + other.x, q.y + other.y, q.z + other.z, q.w + other.w})
|
||||
}
|
||||
|
||||
// ritorna una nuova Quadrica differenza tra il ricevente e un'altra
|
||||
fn (q &Quad) sub(other &Quad) &Quad {
|
||||
return &(Quad{q.x - other.x, q.y - other.y, q.z - other.z, q.w - other.w})
|
||||
}
|
||||
|
||||
// ritorna una nuova Quadrica ottenuta negando il ricevente
|
||||
fn (q &Quad) neg() &Quad {
|
||||
return &(Quad{-q.x, -q.y, -q.z, -q.w})
|
||||
}
|
||||
|
||||
// ritorna una nuova Quadrica ottenuta moltiplicando il ricevente per una costante
|
||||
fn (q &Quad) mult(factor f64) &Quad {
|
||||
return &(Quad{q.x * factor, q.y * factor, q.z * factor, q.w * factor})
|
||||
}
|
||||
|
||||
// ritorna una nuova Quadrica ottenuta dividendo il ricevente per una costante
|
||||
fn (q &Quad) div(factor f64) &Quad {
|
||||
return &(Quad{q.x / factor, q.y / factor, q.z / factor, q.w / factor})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut n := Quad{1, 2, 3, 4}
|
||||
|
||||
println(n)
|
||||
println(n.get(0))
|
||||
println(n.get(1))
|
||||
println(n.get(2))
|
||||
println(n.get(3))
|
||||
n.set(0, 5)
|
||||
n.set(1, 6)
|
||||
n.set(2, 7)
|
||||
n.set(3, 8)
|
||||
println(n)
|
||||
println(n.get(0))
|
||||
println(n.get(1))
|
||||
println(n.get(2))
|
||||
println(n.get(3))
|
||||
}
|
|
@ -7,15 +7,14 @@ import strings
|
|||
|
||||
fn (mut g Gen) get_free_method(typ ast.Type) string {
|
||||
g.autofree_methods[typ] = true
|
||||
styp := g.typ(typ).replace('*', '')
|
||||
mut sym := g.table.sym(g.unwrap_generic(typ))
|
||||
mut fn_name := styp_to_free_fn_name(styp)
|
||||
if mut sym.info is ast.Alias {
|
||||
if sym.info.is_import {
|
||||
sym = g.table.sym(sym.info.parent_type)
|
||||
}
|
||||
}
|
||||
|
||||
styp := g.typ(typ).replace('*', '')
|
||||
fn_name := styp_to_free_fn_name(styp)
|
||||
if sym.has_method_with_generic_parent('free') {
|
||||
return fn_name
|
||||
}
|
||||
|
@ -30,21 +29,23 @@ fn (mut g Gen) gen_free_methods() {
|
|||
|
||||
fn (mut g Gen) gen_free_method(typ ast.Type) string {
|
||||
styp := g.typ(typ).replace('*', '')
|
||||
mut sym := g.table.sym(g.unwrap_generic(typ))
|
||||
mut fn_name := styp_to_free_fn_name(styp)
|
||||
if typ in g.generated_free_methods {
|
||||
deref_typ := typ.set_nr_muls(0)
|
||||
if deref_typ in g.generated_free_methods {
|
||||
return fn_name
|
||||
}
|
||||
g.generated_free_methods[typ] = true
|
||||
g.generated_free_methods[deref_typ] = true
|
||||
|
||||
mut sym := g.table.sym(g.unwrap_generic(typ))
|
||||
if mut sym.info is ast.Alias {
|
||||
if sym.info.is_import {
|
||||
sym = g.table.sym(sym.info.parent_type)
|
||||
}
|
||||
}
|
||||
|
||||
if sym.has_method_with_generic_parent('free') {
|
||||
return fn_name
|
||||
}
|
||||
|
||||
match mut sym.info {
|
||||
ast.Struct {
|
||||
g.gen_free_for_struct(sym.info, styp, fn_name)
|
||||
|
|
|
@ -144,7 +144,7 @@ fn (mut g Gen) get_str_fn(typ ast.Type) string {
|
|||
styp := g.typ(unwrapped)
|
||||
mut sym := g.table.sym(unwrapped)
|
||||
mut str_fn_name := styp_to_str_fn_name(styp)
|
||||
if mut sym.info is ast.Alias {
|
||||
if mut sym.info is ast.Alias && !sym.has_method('str') {
|
||||
if sym.info.is_import {
|
||||
sym = g.table.sym(sym.info.parent_type)
|
||||
str_fn_name = styp_to_str_fn_name(sym.name)
|
||||
|
|
|
@ -20,10 +20,11 @@ const (
|
|||
// `small` should not be needed, but see: https://stackoverflow.com/questions/5874215/what-is-rpcndr-h
|
||||
c_reserved = ['array', 'auto', 'bool', 'break', 'calloc', 'case', 'char', 'class', 'complex',
|
||||
'const', 'continue', 'default', 'delete', 'do', 'double', 'else', 'enum', 'error', 'exit',
|
||||
'export', 'extern', 'float', 'for', 'free', 'goto', 'if', 'inline', 'int', 'link', 'long',
|
||||
'malloc', 'namespace', 'new', 'panic', 'register', 'restrict', 'return', 'short', 'signed',
|
||||
'sizeof', 'static', 'string', 'struct', 'switch', 'typedef', 'typename', 'union', 'unix',
|
||||
'unsigned', 'void', 'volatile', 'while', 'template', 'small', 'stdout', 'stdin', 'stderr']
|
||||
'export', 'extern', 'false', 'float', 'for', 'free', 'goto', 'if', 'inline', 'int', 'link',
|
||||
'long', 'malloc', 'namespace', 'new', 'panic', 'register', 'restrict', 'return', 'short',
|
||||
'signed', 'sizeof', 'static', 'string', 'struct', 'switch', 'typedef', 'typename', 'union',
|
||||
'unix', 'unsigned', 'void', 'volatile', 'while', 'template', 'true', 'small', 'stdout',
|
||||
'stdin', 'stderr']
|
||||
c_reserved_map = string_array_to_map(c_reserved)
|
||||
// same order as in token.Kind
|
||||
cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le']
|
||||
|
@ -955,7 +956,13 @@ fn (mut g Gen) optional_type_name(t ast.Type) (string, string) {
|
|||
|
||||
fn (g Gen) optional_type_text(styp string, base string) string {
|
||||
// replace void with something else
|
||||
size := if base == 'void' { 'byte' } else { base }
|
||||
size := if base == 'void' {
|
||||
'byte'
|
||||
} else if base.starts_with('anon_fn') {
|
||||
'void*'
|
||||
} else {
|
||||
base
|
||||
}
|
||||
ret := 'struct $styp {
|
||||
byte state;
|
||||
IError err;
|
||||
|
@ -1834,7 +1841,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||
}
|
||||
ast.Module {
|
||||
// g.is_builtin_mod = node.name == 'builtin'
|
||||
g.is_builtin_mod = node.name in ['builtin', 'strconv', 'strings', 'dlmalloc']
|
||||
// g.is_builtin_mod = node.name in ['builtin', 'strconv', 'strings', 'dlmalloc']
|
||||
g.is_builtin_mod = util.module_is_builtin(node.name)
|
||||
// g.cur_mod = node.name
|
||||
g.cur_mod = node
|
||||
}
|
||||
|
@ -2037,7 +2045,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
|
|||
&& !expected_type.has_flag(.optional) {
|
||||
if expr is ast.StructInit && !got_type.is_ptr() {
|
||||
g.inside_cast_in_heap++
|
||||
got_styp := g.cc_type(got_type.ref(), true)
|
||||
got_styp := g.cc_type(got_type_raw.ref(), true)
|
||||
// TODO: why does cc_type even add this in the first place?
|
||||
exp_styp := exp_sym.cname
|
||||
mut fname := 'I_${got_styp}_to_Interface_$exp_styp'
|
||||
|
@ -2048,12 +2056,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
|
|||
got_styp)
|
||||
g.inside_cast_in_heap--
|
||||
} else {
|
||||
mut got_styp := g.cc_type(got_type, true)
|
||||
got_styp = match got_styp {
|
||||
'int' { 'int_literal' }
|
||||
'f64' { 'float_literal' }
|
||||
else { got_styp }
|
||||
}
|
||||
got_styp := g.cc_type(got_type_raw, true)
|
||||
got_is_shared := got_type.has_flag(.shared_f)
|
||||
exp_styp := if got_is_shared { '__shared__$exp_sym.cname' } else { exp_sym.cname }
|
||||
// If it's shared, we need to use the other caster:
|
||||
|
@ -3645,11 +3648,23 @@ fn (mut g Gen) ident(node ast.Ident) {
|
|||
g.write(util.no_dots(node.name[2..]))
|
||||
return
|
||||
}
|
||||
if node.kind == .constant { // && !node.name.starts_with('g_') {
|
||||
// TODO globals hack
|
||||
g.write('_const_')
|
||||
}
|
||||
mut name := c_name(node.name)
|
||||
if node.kind == .constant { // && !node.name.starts_with('g_') {
|
||||
if g.pref.translated && !g.is_builtin_mod
|
||||
&& !util.module_is_builtin(node.name.all_before_last('.')) {
|
||||
// Don't prepend "_const" to translated C consts,
|
||||
// but only in user code, continue prepending "_const" to builtin consts.
|
||||
mut x := util.no_dots(node.name)
|
||||
if x.starts_with('main__') {
|
||||
x = x['main__'.len..]
|
||||
}
|
||||
g.write(x)
|
||||
return
|
||||
} else {
|
||||
// TODO globals hack
|
||||
g.write('_const_')
|
||||
}
|
||||
}
|
||||
// TODO: temporary, remove this
|
||||
node_info := node.info
|
||||
mut is_auto_heap := false
|
||||
|
@ -4114,6 +4129,12 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
|
|||
}
|
||||
}
|
||||
name := c_name(field.name)
|
||||
const_name := if node.attrs.contains('export') && !g.is_builtin_mod {
|
||||
// TODO this only works for the first const in the group for now
|
||||
node.attrs[0].arg
|
||||
} else {
|
||||
'_const_' + name
|
||||
}
|
||||
field_expr := field.expr
|
||||
match field.expr {
|
||||
ast.ArrayInit {
|
||||
|
@ -4121,19 +4142,19 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
|
|||
styp := g.typ(field.expr.typ)
|
||||
if g.pref.build_mode != .build_module {
|
||||
val := g.expr_string(field.expr)
|
||||
g.definitions.writeln('$styp _const_$name = $val; // fixed array const')
|
||||
g.definitions.writeln('$styp $const_name = $val; // fixed array const')
|
||||
} else {
|
||||
g.definitions.writeln('$styp _const_$name; // fixed array const')
|
||||
g.definitions.writeln('$styp $const_name; // fixed array const')
|
||||
}
|
||||
} else {
|
||||
g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
|
||||
}
|
||||
}
|
||||
ast.StringLiteral {
|
||||
g.definitions.writeln('string _const_$name; // a string literal, inited later')
|
||||
g.definitions.writeln('string $const_name; // a string literal, inited later')
|
||||
if g.pref.build_mode != .build_module {
|
||||
val := g.expr_string(field.expr)
|
||||
g.stringliterals.writeln('\t_const_$name = $val;')
|
||||
g.stringliterals.writeln('\t$const_name = $val;')
|
||||
}
|
||||
}
|
||||
ast.CallExpr {
|
||||
|
@ -4177,7 +4198,7 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
|
|||
|
||||
fn (mut g Gen) const_decl_precomputed(mod string, name string, ct_value ast.ComptTimeConstValue, typ ast.Type) bool {
|
||||
mut styp := g.typ(typ)
|
||||
cname := '_const_$name'
|
||||
cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_$name' }
|
||||
$if trace_const_precomputed ? {
|
||||
eprintln('> styp: $styp | cname: $cname | ct_value: $ct_value | $ct_value.type_name()')
|
||||
}
|
||||
|
@ -4246,7 +4267,7 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, ct_value ast.Comp
|
|||
// TODO: ^ the above for strings, cause:
|
||||
// `error C2099: initializer is not a constant` errors in MSVC,
|
||||
// so fall back to the delayed initialisation scheme:
|
||||
g.definitions.writeln('$styp $cname; // inited later')
|
||||
g.definitions.writeln('$styp $cname; // str inited later')
|
||||
g.init.writeln('\t$cname = _SLIT("$escaped_val");')
|
||||
if g.is_autofree {
|
||||
g.cleanups[mod].writeln('\tstring_free(&$cname);')
|
||||
|
@ -4276,7 +4297,7 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ
|
|||
// Initialize more complex consts in `void _vinit/2{}`
|
||||
// (C doesn't allow init expressions that can't be resolved at compile time).
|
||||
mut styp := g.typ(typ)
|
||||
cname := '_const_$name'
|
||||
cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_$name' }
|
||||
g.definitions.writeln('$styp $cname; // inited later')
|
||||
if cname == '_const_os__args' {
|
||||
if g.pref.os == .windows {
|
||||
|
@ -4339,6 +4360,13 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
|
|||
}
|
||||
}
|
||||
styp := g.typ(field.typ)
|
||||
mut anon_fn_expr := unsafe { field.expr }
|
||||
if field.has_expr && mut anon_fn_expr is ast.AnonFn {
|
||||
g.gen_anon_fn_decl(mut anon_fn_expr)
|
||||
fn_type_name := g.get_anon_fn_type_name(mut anon_fn_expr, field.name)
|
||||
g.definitions.writeln('$fn_type_name = ${g.table.sym(field.typ).name}; // global')
|
||||
continue
|
||||
}
|
||||
g.definitions.write_string('$visibility_kw$styp $attributes $field.name')
|
||||
if field.has_expr {
|
||||
if field.expr.is_literal() && should_init {
|
||||
|
@ -4524,17 +4552,17 @@ fn (mut g Gen) write_builtin_types() {
|
|||
// Sort the types, make sure types that are referenced by other types
|
||||
// are added before them.
|
||||
fn (mut g Gen) write_sorted_types() {
|
||||
g.type_definitions.writeln('// #start sorted_symbols')
|
||||
defer {
|
||||
g.type_definitions.writeln('// #end sorted_symbols')
|
||||
}
|
||||
mut symbols := []&ast.TypeSymbol{cap: g.table.type_symbols.len} // structs that need to be sorted
|
||||
for sym in g.table.type_symbols {
|
||||
if sym.name !in c.builtins {
|
||||
symbols << sym
|
||||
}
|
||||
}
|
||||
// sort structs
|
||||
sorted_symbols := g.sort_structs(symbols)
|
||||
// Generate C code
|
||||
g.type_definitions.writeln('// builtin types:')
|
||||
g.type_definitions.writeln('//------------------ #endbuiltin')
|
||||
g.write_types(sorted_symbols)
|
||||
}
|
||||
|
||||
|
@ -4706,7 +4734,7 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
|
|||
}
|
||||
|
||||
// sort structs by dependant fields
|
||||
fn (g &Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol {
|
||||
fn (mut g Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol {
|
||||
util.timing_start(@METHOD)
|
||||
defer {
|
||||
util.timing_measure(@METHOD)
|
||||
|
@ -4742,12 +4770,23 @@ fn (g &Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol {
|
|||
field_deps << dep
|
||||
}
|
||||
for field in sym.info.fields {
|
||||
dep := g.table.sym(field.typ).name
|
||||
if field.typ.is_ptr() {
|
||||
continue
|
||||
}
|
||||
fsym := g.table.sym(field.typ)
|
||||
dep := fsym.name
|
||||
// skip if not in types list or already in deps
|
||||
if dep !in type_names || dep in field_deps || field.typ.is_ptr() {
|
||||
if dep !in type_names || dep in field_deps {
|
||||
continue
|
||||
}
|
||||
field_deps << dep
|
||||
if fsym.info is ast.Alias {
|
||||
xdep := g.table.sym(fsym.info.parent_type).name
|
||||
if xdep !in type_names || xdep in field_deps {
|
||||
continue
|
||||
}
|
||||
field_deps << xdep
|
||||
}
|
||||
}
|
||||
}
|
||||
// ast.Interface {}
|
||||
|
@ -4795,7 +4834,7 @@ fn (mut g Gen) go_before_ternary() string {
|
|||
}
|
||||
|
||||
fn (mut g Gen) insert_before_stmt(s string) {
|
||||
cur_line := g.go_before_stmt(0)
|
||||
cur_line := g.go_before_stmt(g.inside_ternary)
|
||||
g.writeln(s)
|
||||
g.write(cur_line)
|
||||
}
|
||||
|
@ -4815,12 +4854,15 @@ fn (mut g Gen) insert_at(pos int, s string) {
|
|||
// Returns the type of the last stmt
|
||||
fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Type) {
|
||||
cvar_name := c_name(var_name)
|
||||
mr_styp := g.base_type(return_type)
|
||||
mut mr_styp := g.base_type(return_type)
|
||||
is_none_ok := return_type == ast.ovoid_type
|
||||
g.writeln(';')
|
||||
if is_none_ok {
|
||||
g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._typ != _IError_None___index) {')
|
||||
} else {
|
||||
if return_type != 0 && g.table.sym(return_type).kind == .function {
|
||||
mr_styp = 'voidptr'
|
||||
}
|
||||
g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ')
|
||||
}
|
||||
if or_block.kind == .block {
|
||||
|
@ -5106,6 +5148,8 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
|
|||
} else if mut expr.left is ast.AnonFn {
|
||||
g.gen_anon_fn_decl(mut expr.left)
|
||||
name = expr.left.decl.name
|
||||
} else if expr.is_fn_var {
|
||||
name = g.table.sym(expr.fn_var_type).name
|
||||
}
|
||||
name = util.no_dots(name)
|
||||
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
|
||||
|
@ -5119,7 +5163,8 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
|
|||
panic('cgen: obf name "$key" not found, this should never happen')
|
||||
}
|
||||
}
|
||||
g.writeln('// go')
|
||||
g.empty_line = true
|
||||
g.writeln('// start go')
|
||||
wrapper_struct_name := 'thread_arg_' + name
|
||||
wrapper_fn_name := name + '_thread_wrapper'
|
||||
arg_tmp_var := 'arg_' + tmp
|
||||
|
@ -5158,7 +5203,7 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
|
|||
} else {
|
||||
'thread_$tmp'
|
||||
}
|
||||
g.writeln('HANDLE $simple_handle = CreateThread(0,0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0,0);')
|
||||
g.writeln('HANDLE $simple_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0, 0);')
|
||||
g.writeln('if (!$simple_handle) panic_lasterr(tos3("`go ${name}()`: "));')
|
||||
if node.is_expr && node.call_expr.return_type != ast.void_type {
|
||||
g.writeln('$gohandle_name thread_$tmp = {')
|
||||
|
@ -5177,7 +5222,7 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
|
|||
g.writeln('pthread_detach(thread_$tmp);')
|
||||
}
|
||||
}
|
||||
g.writeln('// endgo\n')
|
||||
g.writeln('// end go')
|
||||
if node.is_expr {
|
||||
handle = 'thread_$tmp'
|
||||
// create wait handler for this return type if none exists
|
||||
|
@ -5643,7 +5688,8 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
|
|||
if fargs.len > 1 {
|
||||
methods_wrapper.write_string(', ')
|
||||
}
|
||||
methods_wrapper.writeln('${fargs[1..].join(', ')});')
|
||||
args := fargs[1..].join(', ')
|
||||
methods_wrapper.writeln('$args);')
|
||||
} else {
|
||||
if parameter_name.starts_with('__shared__') {
|
||||
methods_wrapper.writeln('${method_call}(${fargs.join(', ')}->val);')
|
||||
|
|
|
@ -203,7 +203,7 @@ fn cgen_attrs(attrs []ast.Attr) []string {
|
|||
|
||||
fn (mut g Gen) comptime_at(node ast.AtExpr) {
|
||||
if node.kind == .vmod_file {
|
||||
val := cnewlines(node.val.replace('\r', ''))
|
||||
val := cescape_nonascii(util.smart_quote(node.val, false))
|
||||
g.write('_SLIT("$val")')
|
||||
} else {
|
||||
val := node.val.replace('\\', '\\\\')
|
||||
|
|
|
@ -612,6 +612,25 @@ fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic
|
|||
return fargs, fargtypes, heap_promoted
|
||||
}
|
||||
|
||||
fn (mut g Gen) get_anon_fn_type_name(mut node ast.AnonFn, var_name string) string {
|
||||
mut builder := strings.new_builder(64)
|
||||
return_styp := g.typ(node.decl.return_type)
|
||||
builder.write_string('$return_styp (*$var_name) (')
|
||||
if node.decl.params.len == 0 {
|
||||
builder.write_string('void)')
|
||||
} else {
|
||||
for i, param in node.decl.params {
|
||||
param_styp := g.typ(param.typ)
|
||||
builder.write_string('$param_styp $param.name')
|
||||
if i != node.decl.params.len - 1 {
|
||||
builder.write_string(', ')
|
||||
}
|
||||
}
|
||||
builder.write_string(')')
|
||||
}
|
||||
return builder.str()
|
||||
}
|
||||
|
||||
fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||
// g.write('/*call expr*/')
|
||||
// NOTE: everything could be done this way
|
||||
|
|
|
@ -115,92 +115,91 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
|
|||
cvar_name := guard_vars[guard_idx]
|
||||
g.writeln('\tIError err = ${cvar_name}.err;')
|
||||
}
|
||||
} else {
|
||||
match branch.cond {
|
||||
ast.IfGuardExpr {
|
||||
mut var_name := guard_vars[i]
|
||||
mut short_opt := false
|
||||
if var_name == '' {
|
||||
short_opt = true // we don't need a further tmp, so use the one we'll get later
|
||||
var_name = g.new_tmp_var()
|
||||
guard_vars[i] = var_name // for `else`
|
||||
g.tmp_count--
|
||||
g.writeln('if (${var_name}.state == 0) {')
|
||||
} else if branch.cond is ast.IfGuardExpr {
|
||||
mut var_name := guard_vars[i]
|
||||
mut short_opt := false
|
||||
if var_name == '' {
|
||||
short_opt = true // we don't need a further tmp, so use the one we'll get later
|
||||
var_name = g.new_tmp_var()
|
||||
guard_vars[i] = var_name // for `else`
|
||||
g.tmp_count--
|
||||
g.writeln('if (${var_name}.state == 0) {')
|
||||
} else {
|
||||
g.write('if ($var_name = ')
|
||||
g.expr(branch.cond.expr)
|
||||
g.writeln(', ${var_name}.state == 0) {')
|
||||
}
|
||||
if short_opt || branch.cond.vars[0].name != '_' {
|
||||
base_type := g.base_type(branch.cond.expr_type)
|
||||
if short_opt {
|
||||
cond_var_name := if branch.cond.vars[0].name == '_' {
|
||||
'_dummy_${g.tmp_count + 1}'
|
||||
} else {
|
||||
g.write('if ($var_name = ')
|
||||
g.expr(branch.cond.expr)
|
||||
g.writeln(', ${var_name}.state == 0) {')
|
||||
branch.cond.vars[0].name
|
||||
}
|
||||
if short_opt || branch.cond.vars[0].name != '_' {
|
||||
base_type := g.base_type(branch.cond.expr_type)
|
||||
if short_opt {
|
||||
cond_var_name := if branch.cond.vars[0].name == '_' {
|
||||
'_dummy_${g.tmp_count + 1}'
|
||||
} else {
|
||||
branch.cond.vars[0].name
|
||||
}
|
||||
g.write('\t$base_type $cond_var_name = ')
|
||||
g.expr(branch.cond.expr)
|
||||
g.writeln(';')
|
||||
g.write('\t$base_type $cond_var_name = ')
|
||||
g.expr(branch.cond.expr)
|
||||
g.writeln(';')
|
||||
} else {
|
||||
mut is_auto_heap := false
|
||||
if branch.stmts.len > 0 {
|
||||
scope := g.file.scope.innermost(ast.Node(branch.stmts[branch.stmts.len - 1]).pos().pos)
|
||||
if v := scope.find_var(branch.cond.vars[0].name) {
|
||||
is_auto_heap = v.is_auto_heap
|
||||
}
|
||||
}
|
||||
if branch.cond.vars.len == 1 {
|
||||
left_var_name := c_name(branch.cond.vars[0].name)
|
||||
if is_auto_heap {
|
||||
g.writeln('\t$base_type* $left_var_name = HEAP($base_type, *($base_type*)${var_name}.data);')
|
||||
} else {
|
||||
mut is_auto_heap := false
|
||||
if branch.stmts.len > 0 {
|
||||
scope := g.file.scope.innermost(ast.Node(branch.stmts[branch.stmts.len - 1]).pos().pos)
|
||||
if v := scope.find_var(branch.cond.vars[0].name) {
|
||||
is_auto_heap = v.is_auto_heap
|
||||
}
|
||||
}
|
||||
if branch.cond.vars.len == 1 {
|
||||
left_var_name := c_name(branch.cond.vars[0].name)
|
||||
if is_auto_heap {
|
||||
g.writeln('\t$base_type* $left_var_name = HEAP($base_type, *($base_type*)${var_name}.data);')
|
||||
} else {
|
||||
g.writeln('\t$base_type $left_var_name = *($base_type*)${var_name}.data;')
|
||||
}
|
||||
} else if branch.cond.vars.len > 1 {
|
||||
for vi, var in branch.cond.vars {
|
||||
left_var_name := c_name(var.name)
|
||||
sym := g.table.sym(branch.cond.expr_type)
|
||||
if sym.kind == .multi_return {
|
||||
mr_info := sym.info as ast.MultiReturn
|
||||
if mr_info.types.len == branch.cond.vars.len {
|
||||
var_typ := g.typ(mr_info.types[vi])
|
||||
if is_auto_heap {
|
||||
g.writeln('\t$var_typ* $left_var_name = (HEAP($base_type, *($base_type*)${var_name}.data).arg$vi);')
|
||||
} else {
|
||||
g.writeln('\t$var_typ $left_var_name = (*($base_type*)${var_name}.data).arg$vi;')
|
||||
}
|
||||
}
|
||||
g.writeln('\t$base_type $left_var_name = *($base_type*)${var_name}.data;')
|
||||
}
|
||||
} else if branch.cond.vars.len > 1 {
|
||||
for vi, var in branch.cond.vars {
|
||||
left_var_name := c_name(var.name)
|
||||
sym := g.table.sym(branch.cond.expr_type)
|
||||
if sym.kind == .multi_return {
|
||||
mr_info := sym.info as ast.MultiReturn
|
||||
if mr_info.types.len == branch.cond.vars.len {
|
||||
var_typ := g.typ(mr_info.types[vi])
|
||||
if is_auto_heap {
|
||||
g.writeln('\t$var_typ* $left_var_name = (HEAP($base_type, *($base_type*)${var_name}.data).arg$vi);')
|
||||
} else {
|
||||
g.writeln('\t$var_typ $left_var_name = (*($base_type*)${var_name}.data).arg$vi;')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mut no_needs_par := false
|
||||
if branch.cond is ast.InfixExpr {
|
||||
if branch.cond.op == .key_in && branch.cond.left !is ast.InfixExpr
|
||||
&& branch.cond.right is ast.ArrayInit {
|
||||
no_needs_par = true
|
||||
}
|
||||
}
|
||||
if no_needs_par {
|
||||
g.write('if ')
|
||||
} else {
|
||||
g.write('if (')
|
||||
}
|
||||
g.expr(branch.cond)
|
||||
if no_needs_par {
|
||||
g.writeln(' {')
|
||||
} else {
|
||||
g.writeln(') {')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mut no_needs_par := false
|
||||
if branch.cond is ast.InfixExpr {
|
||||
if branch.cond.op == .key_in && branch.cond.left !is ast.InfixExpr
|
||||
&& branch.cond.right is ast.ArrayInit {
|
||||
no_needs_par = true
|
||||
}
|
||||
}
|
||||
if no_needs_par {
|
||||
g.write('if ')
|
||||
} else {
|
||||
g.write('if (')
|
||||
}
|
||||
g.expr(branch.cond)
|
||||
if no_needs_par {
|
||||
g.writeln(' {')
|
||||
} else {
|
||||
g.writeln(') {')
|
||||
}
|
||||
}
|
||||
if needs_tmp_var {
|
||||
if node.is_expr && g.table.sym(node.typ).kind == .sum_type {
|
||||
g.expected_cast_type = node.typ
|
||||
}
|
||||
g.stmts_with_tmp_var(branch.stmts, tmp)
|
||||
g.expected_cast_type = 0
|
||||
} else {
|
||||
// restore if_expr stmt header pos
|
||||
stmt_pos := g.nth_stmt_pos(0)
|
||||
|
|
|
@ -62,6 +62,7 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
|
|||
mut tmp_opt := ''
|
||||
mut cur_line := ''
|
||||
mut gen_or := node.or_expr.kind != .absent || node.is_option
|
||||
mut tmp_left := ''
|
||||
|
||||
if sym.kind == .string {
|
||||
if node.is_gated {
|
||||
|
@ -82,6 +83,15 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
|
|||
}
|
||||
g.expr(node.left)
|
||||
} else if sym.kind == .array {
|
||||
if !range.has_high {
|
||||
tmp_left = g.new_tmp_var()
|
||||
tmp_type := g.typ(node.left_type)
|
||||
g.insert_before_stmt('${util.tabs(g.indent)}$tmp_type $tmp_left;')
|
||||
// (tmp = expr, array_slice(...))
|
||||
g.write('($tmp_left = ')
|
||||
g.expr(node.left)
|
||||
g.write(', ')
|
||||
}
|
||||
if node.is_gated {
|
||||
g.write('array_slice_ni(')
|
||||
} else {
|
||||
|
@ -90,7 +100,11 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
|
|||
if node.left_type.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
if range.has_high {
|
||||
g.expr(node.left)
|
||||
} else {
|
||||
g.write(tmp_left)
|
||||
}
|
||||
} else if sym.kind == .array_fixed {
|
||||
// Convert a fixed array to V array when doing `fixed_arr[start..end]`
|
||||
info := sym.info as ast.ArrayFixed
|
||||
|
@ -101,17 +115,8 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
|
|||
g.write('array_slice(')
|
||||
}
|
||||
g.write('new_array_from_c_array${noscan}(')
|
||||
g.write('$info.size')
|
||||
g.write(', $info.size')
|
||||
g.write(', sizeof(')
|
||||
if node.left_type.is_ptr() {
|
||||
g.write('(*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
if node.left_type.is_ptr() {
|
||||
g.write(')')
|
||||
}
|
||||
g.write('[0]), ')
|
||||
ctype := g.typ(info.elem_type)
|
||||
g.write('$info.size, $info.size, sizeof($ctype), ')
|
||||
if node.left_type.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
|
@ -132,15 +137,17 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
|
|||
} else if sym.kind == .array_fixed {
|
||||
info := sym.info as ast.ArrayFixed
|
||||
g.write('$info.size')
|
||||
} else if node.left_type.is_ptr() {
|
||||
g.write('(')
|
||||
g.write('*')
|
||||
g.expr(node.left)
|
||||
g.write(')')
|
||||
g.write('.len')
|
||||
} else if sym.kind == .array {
|
||||
if node.left_type.is_ptr() {
|
||||
g.write('$tmp_left->')
|
||||
} else {
|
||||
g.write('${tmp_left}.')
|
||||
}
|
||||
g.write('len)')
|
||||
} else {
|
||||
g.write('(')
|
||||
g.expr(node.left)
|
||||
g.write('.len')
|
||||
g.write(').len')
|
||||
}
|
||||
g.write(')')
|
||||
|
||||
|
@ -157,23 +164,22 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
gen_or := node.or_expr.kind != .absent || node.is_option
|
||||
left_is_ptr := node.left_type.is_ptr()
|
||||
info := sym.info as ast.Array
|
||||
elem_type_str := g.typ(info.elem_type)
|
||||
mut elem_type_str := g.typ(info.elem_type)
|
||||
elem_type := info.elem_type
|
||||
elem_typ := g.table.sym(elem_type)
|
||||
elem_sym := g.table.sym(elem_type)
|
||||
if elem_sym.kind == .function {
|
||||
elem_type_str = 'voidptr'
|
||||
}
|
||||
// `vals[i].field = x` is an exception and requires `array_get`:
|
||||
// `(*(Val*)array_get(vals, i)).field = x;`
|
||||
is_selector := node.left is ast.SelectorExpr
|
||||
if g.is_assign_lhs && !is_selector && node.is_setter {
|
||||
is_direct_array_access := (g.fn_decl != 0 && g.fn_decl.is_direct_arr) || node.is_direct
|
||||
is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type
|
||||
array_ptr_type_str := match elem_typ.kind {
|
||||
.function { 'voidptr*' }
|
||||
else { '$elem_type_str*' }
|
||||
}
|
||||
if is_direct_array_access {
|
||||
g.write('(($array_ptr_type_str)')
|
||||
g.write('(($elem_type_str*)')
|
||||
} else if is_op_assign {
|
||||
g.write('(*($array_ptr_type_str)array_get(')
|
||||
g.write('(*($elem_type_str*)array_get(')
|
||||
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('*')
|
||||
}
|
||||
|
@ -217,11 +223,7 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
}
|
||||
*/
|
||||
if need_wrapper {
|
||||
if elem_typ.kind == .function {
|
||||
g.write(', &(voidptr[]) { ')
|
||||
} else {
|
||||
g.write(', &($elem_type_str[]) { ')
|
||||
}
|
||||
g.write(', &($elem_type_str[]) { ')
|
||||
} else {
|
||||
g.write(', &')
|
||||
}
|
||||
|
@ -232,10 +234,6 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
}
|
||||
} else {
|
||||
is_direct_array_access := (g.fn_decl != 0 && g.fn_decl.is_direct_arr) || node.is_direct
|
||||
array_ptr_type_str := match elem_typ.kind {
|
||||
.function { 'voidptr*' }
|
||||
else { '$elem_type_str*' }
|
||||
}
|
||||
// do not clone inside `opt_ok(opt_ok(&(string[]) {..})` before returns
|
||||
needs_clone := info.elem_type == ast.string_type_idx && g.is_autofree && !(g.inside_return
|
||||
&& g.fn_decl.return_type.has_flag(.optional)) && !g.is_assign_lhs
|
||||
|
@ -250,24 +248,24 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
|
||||
tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' }
|
||||
if gen_or {
|
||||
g.write('$array_ptr_type_str $tmp_opt_ptr = ($array_ptr_type_str)/*ee elem_ptr_typ */(array_get_with_check(')
|
||||
g.write('$elem_type_str* $tmp_opt_ptr = ($elem_type_str*)/*ee elem_ptr_typ */(array_get_with_check(')
|
||||
} else {
|
||||
if needs_clone {
|
||||
g.write('/*2*/string_clone(')
|
||||
}
|
||||
if g.is_fn_index_call {
|
||||
if elem_typ.info is ast.FnType {
|
||||
if elem_sym.info is ast.FnType {
|
||||
g.write('((')
|
||||
g.write_fn_ptr_decl(&elem_typ.info, '')
|
||||
g.write(')(*($array_ptr_type_str)/*ee elem_typ */array_get(')
|
||||
g.write_fn_ptr_decl(&elem_sym.info, '')
|
||||
g.write(')(*($elem_type_str*)/*ee elem_sym */array_get(')
|
||||
}
|
||||
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('*')
|
||||
}
|
||||
} else if is_direct_array_access {
|
||||
g.write('(($array_ptr_type_str)')
|
||||
g.write('(($elem_type_str*)')
|
||||
} else {
|
||||
g.write('(*($array_ptr_type_str)/*ee elem_typ */array_get(')
|
||||
g.write('(*($elem_type_str*)/*ee elem_sym */array_get(')
|
||||
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('*')
|
||||
}
|
||||
|
@ -358,9 +356,12 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
info := sym.info as ast.Map
|
||||
key_type_str := g.typ(info.key_type)
|
||||
elem_type := info.value_type
|
||||
elem_type_str := g.typ(elem_type)
|
||||
elem_typ := g.table.sym(elem_type)
|
||||
get_and_set_types := elem_typ.kind in [.struct_, .map]
|
||||
mut elem_type_str := g.typ(elem_type)
|
||||
elem_sym := g.table.sym(elem_type)
|
||||
if elem_sym.kind == .function {
|
||||
elem_type_str = 'voidptr'
|
||||
}
|
||||
get_and_set_types := elem_sym.kind in [.struct_, .map]
|
||||
if g.is_assign_lhs && !g.is_arraymap_set && !get_and_set_types {
|
||||
if g.assign_op == .assign || info.value_type == ast.string_type {
|
||||
g.is_arraymap_set = true
|
||||
|
@ -394,12 +395,8 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
g.is_arraymap_set = old_is_arraymap_set
|
||||
g.is_assign_lhs = old_is_assign_lhs
|
||||
g.write('}')
|
||||
if elem_typ.kind == .function {
|
||||
g.write(', &(voidptr[]) { ')
|
||||
} else {
|
||||
g.arraymap_set_pos = g.out.len
|
||||
g.write(', &($elem_type_str[]) { ')
|
||||
}
|
||||
g.arraymap_set_pos = g.out.len
|
||||
g.write(', &($elem_type_str[]) { ')
|
||||
if g.assign_op != .assign && info.value_type != ast.string_type {
|
||||
zero := g.type_default(info.value_type)
|
||||
g.write('$zero })))')
|
||||
|
@ -438,13 +435,11 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
g.write('$elem_type_str* $tmp_opt_ptr = ($elem_type_str*)/*ee elem_ptr_typ */(map_get_check(')
|
||||
} else {
|
||||
if g.is_fn_index_call {
|
||||
if elem_typ.info is ast.FnType {
|
||||
if elem_sym.info is ast.FnType {
|
||||
g.write('((')
|
||||
g.write_fn_ptr_decl(&elem_typ.info, '')
|
||||
g.write_fn_ptr_decl(&elem_sym.info, '')
|
||||
g.write(')(*(voidptr*)map_get(')
|
||||
}
|
||||
} else if elem_typ.kind == .function {
|
||||
g.write('(*(voidptr*)map_get(')
|
||||
} else {
|
||||
g.write('(*($elem_type_str*)map_get(')
|
||||
}
|
||||
|
@ -470,8 +465,6 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
|
|||
g.write('))')
|
||||
} else if g.is_fn_index_call {
|
||||
g.write(', &(voidptr[]){ $zero })))')
|
||||
} else if elem_typ.kind == .function {
|
||||
g.write(', &(voidptr[]){ $zero }))')
|
||||
} else {
|
||||
g.write(', &($elem_type_str[]){ $zero }))')
|
||||
}
|
||||
|
|
|
@ -507,7 +507,7 @@ fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
|
|||
else { ast.Type(0) }
|
||||
}
|
||||
sub_sym := g.table.sym(sub_type)
|
||||
g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index')
|
||||
g.write('_${sym.cname}_${sub_sym.cname}_index')
|
||||
return
|
||||
} else if sym.kind == .sum_type {
|
||||
g.write('_typ $cmp_op ')
|
||||
|
|
|
@ -233,7 +233,7 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
|||
}
|
||||
g.writeln(') {')
|
||||
g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
|
||||
g.writeln('break;')
|
||||
g.writeln('\tbreak;')
|
||||
g.writeln('}')
|
||||
}
|
||||
g.indent--
|
||||
|
@ -259,7 +259,8 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
|||
}
|
||||
g.stmts_with_tmp_var(branch.stmts, tmp_var)
|
||||
g.expected_cast_type = 0
|
||||
g.writeln('} break;')
|
||||
g.writeln('\tbreak;')
|
||||
g.writeln('}')
|
||||
g.indent--
|
||||
}
|
||||
if range_branches.len > 0 && !default_generated {
|
||||
|
@ -297,7 +298,7 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
|||
}
|
||||
g.writeln(') {')
|
||||
g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
|
||||
g.writeln('break;')
|
||||
g.writeln('\tbreak;')
|
||||
g.writeln('}')
|
||||
}
|
||||
g.indent--
|
||||
|
|
|
@ -70,8 +70,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
|||
typ = typ.clear_flag(.shared_f).set_nr_muls(0)
|
||||
}
|
||||
mut sym := g.table.sym(typ)
|
||||
// when type is alias, print the aliased value
|
||||
if mut sym.info is ast.Alias {
|
||||
// when type is alias and doesn't has `str()`, print the aliased value
|
||||
if mut sym.info is ast.Alias && !sym.has_method('str') {
|
||||
parent_sym := g.table.sym(sym.info.parent_type)
|
||||
if parent_sym.has_method('str') {
|
||||
typ = sym.info.parent_type
|
||||
|
|
|
@ -604,6 +604,13 @@ fn (mut p Parser) prefix_expr() ast.Expr {
|
|||
return right
|
||||
}
|
||||
}
|
||||
if mut right is ast.ParExpr {
|
||||
if right.expr is ast.StructInit {
|
||||
p.note_with_pos('unnecessary `()`, use `&$right.expr` instead of `&($right.expr)`',
|
||||
right.pos)
|
||||
right = right.expr
|
||||
}
|
||||
}
|
||||
}
|
||||
mut or_stmts := []ast.Stmt{}
|
||||
mut or_kind := ast.OrKind.absent
|
||||
|
|
|
@ -107,6 +107,10 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
|
|||
p.check(.decl_assign)
|
||||
comments << p.eat_comments()
|
||||
expr := p.expr(0)
|
||||
if expr !in [ast.CallExpr, ast.IndexExpr, ast.PrefixExpr] {
|
||||
p.error_with_pos('if guard condition expression is illegal, it should return optional',
|
||||
expr.pos())
|
||||
}
|
||||
|
||||
cond = ast.IfGuardExpr{
|
||||
vars: vars
|
||||
|
|
|
@ -1943,6 +1943,7 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
|||
p.register_auto_import('sync')
|
||||
}
|
||||
mut_pos := p.tok.pos()
|
||||
modifier_kind := p.tok.kind
|
||||
is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
|
||||
if is_mut {
|
||||
p.next()
|
||||
|
@ -1956,7 +1957,11 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
|||
p.next()
|
||||
}
|
||||
if p.tok.kind != .name {
|
||||
p.error('unexpected token `$p.tok.lit`')
|
||||
if is_mut || is_static || is_volatile {
|
||||
p.error_with_pos('the `$modifier_kind` keyword is invalid here', mut_pos)
|
||||
} else {
|
||||
p.error('unexpected token `$p.tok.lit`')
|
||||
}
|
||||
return ast.Ident{
|
||||
scope: p.scope
|
||||
}
|
||||
|
@ -2190,7 +2195,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
}
|
||||
// Raw string (`s := r'hello \n ')
|
||||
if p.peek_tok.kind == .string && !p.inside_str_interp && p.peek_token(2).kind != .colon {
|
||||
if p.tok.lit in ['r', 'c', 'js'] && p.tok.kind == .name {
|
||||
if p.tok.kind == .name && p.tok.lit in ['r', 'c', 'js'] {
|
||||
return p.string_expr()
|
||||
} else {
|
||||
// don't allow any other string prefix except `r`, `js` and `c`
|
||||
|
@ -2198,7 +2203,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
}
|
||||
}
|
||||
// don't allow r`byte` and c`byte`
|
||||
if p.tok.lit in ['r', 'c'] && p.peek_tok.kind == .chartoken {
|
||||
if p.peek_tok.kind == .chartoken && p.tok.lit.len == 1 && p.tok.lit[0] in [`r`, `c`] {
|
||||
opt := if p.tok.lit == 'r' { '`r` (raw string)' } else { '`c` (c string)' }
|
||||
return p.error('cannot use $opt with `byte` and `rune`')
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/parser/tests/if_guard_cond_err.vv:16:16: error: if guard condition expression is illegal, it should return optional
|
||||
14 | fp.usage_example('GOOG AAPL')
|
||||
15 | _ := fp.bool('version', `v`, false, 'version information.')
|
||||
16 | if args := fp.finalize() && args.len > 0 {
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
17 | return args
|
||||
18 | } else {
|
|
@ -0,0 +1,28 @@
|
|||
import os
|
||||
import flag
|
||||
|
||||
const version = "v0.1.0"
|
||||
|
||||
// getting command line options and arguments
|
||||
// returns the arguments
|
||||
fn get_args() ?[]string {
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.application('ticker')
|
||||
fp.version(version)
|
||||
fp.description('A CLI yahoo ticker app')
|
||||
fp.skip_executable()
|
||||
fp.usage_example('GOOG AAPL')
|
||||
_ := fp.bool('version', `v`, false, 'version information.')
|
||||
if args := fp.finalize() && args.len > 0 {
|
||||
return args
|
||||
} else {
|
||||
eprintln(err.msg())
|
||||
println(fp.usage())
|
||||
return none
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tickers := get_args() or { return }
|
||||
println(tickers)
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/parser/tests/invalid_using_atomic.vv:2:5: error: the `atomic` keyword is invalid here
|
||||
1 | fn main() {
|
||||
2 | if atomic true {
|
||||
| ~~~~~~
|
||||
3 | println(true)
|
||||
4 | }
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue