From 8dc2601080ca6a8453c383c4ac93e740e2824d5b Mon Sep 17 00:00:00 2001 From: Joe Conigliaro Date: Fri, 8 Apr 2022 17:33:24 +1000 Subject: [PATCH 01/30] all: comptime_call - simplify tmpl scoping, solves many issues. --- vlib/v/checker/checker.v | 13 +++++++--- vlib/v/checker/comptime.v | 22 +--------------- vlib/v/gen/c/cgen.v | 6 ++--- vlib/v/parser/comptime.v | 26 +++---------------- vlib/v/parser/parser.v | 3 ++- ...comptime_call_tmpl_variable_scope_test.tpl | 2 ++ .../comptime_call_tmpl_variable_scope_test.v | 11 ++++++++ 7 files changed, 32 insertions(+), 51 deletions(-) create mode 100644 vlib/v/tests/comptime_call_tmpl_variable_scope_test.tpl create mode 100644 vlib/v/tests/comptime_call_tmpl_variable_scope_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0fc6104eab..9ea39054b2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -124,6 +124,7 @@ mut: inside_println_arg bool inside_decl_rhs bool inside_if_guard bool // true inside the guard condition of `if x := opt() {}` + vweb_comptime_call_pos int // needed for correctly checking use before decl for vweb templates } pub fn new_checker(table &ast.Table, pref &pref.Preferences) &Checker { @@ -3104,9 +3105,15 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type { return obj.typ } ast.Var { - // incase var was not marked as used yet (vweb tmpl) - // obj.is_used = true - if node.pos.pos < obj.pos.pos { + // inside vweb tmpl ident positions are meaningless, use the position of the comptime call. + // if the variable is declared before the comptime call then we can assume all is well. + // `node.name !in node.scope.objects` checks it's an inherited var (not defined in the tmpl). + node_pos := if c.pref.is_vweb && node.name !in node.scope.objects { + c.vweb_comptime_call_pos + } else { + node.pos.pos + } + if node_pos < obj.pos.pos { c.error('undefined variable `$node.name` (used before declaration)', node.pos) } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 4dd5ec3af7..aff684ef00 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -36,28 +36,8 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { is_vweb: true } mut c2 := new_checker(c.table, pref2) + c2.vweb_comptime_call_pos = node.pos.pos c2.check(node.vweb_tmpl) - mut caller_scope := c.fn_scope.innermost(node.pos.pos) - mut i := 0 // tmp counter var for skipping first three tmpl vars - for k, _ in c2.file.scope.children[0].objects { - if i < 2 { - // Skip first three because they are tmpl vars see vlib/vweb/tmpl/tmpl.v - i++ - continue - } - tmpl_obj := unsafe { c2.file.scope.children[0].objects[k] } - if tmpl_obj is ast.Var { - if mut caller_var := caller_scope.find_var(tmpl_obj.name) { - // var is used in the tmpl so mark it as used in the caller - caller_var.is_used = true - // update props from the caller scope var to the tmpl scope var - c2.file.scope.children[0].objects[k] = ast.Var{ - ...(*caller_var) - pos: tmpl_obj.pos - } - } - } - } c.warnings << c2.warnings c.errors << c2.errors c.notices << c2.notices diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index c77803429a..7cc472feb7 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3697,10 +3697,8 @@ fn (mut g Gen) ident(node ast.Ident) { g.write('${name}.val') return } - // TODO: investigate why node.obj is pointing to outdated ScopeObject? - // v := node.obj - // if v is ast.Var { - if v := node.scope.find_var(node.name) { + v := node.obj + if v is ast.Var { is_auto_heap = v.is_auto_heap && (!g.is_assign_lhs || g.assign_op != .decl_assign) if is_auto_heap { g.write('(*(') diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 6d07664354..76752823e8 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -245,10 +245,6 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { println('$path:${i + 1}: $line') } } - mut scope := &ast.Scope{ - start_pos: 0 - parent: p.table.global_scope - } $if trace_comptime ? { println('') println('>>> template for $path:') @@ -256,25 +252,11 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { println('>>> end of template END') println('') } - mut file := parse_comptime(v_code, p.table, p.pref, scope) + // the tmpl inherits all parent scopes. previous functionality was just to + // inherit the scope from which the comptime call was made and no parents. + // this is much simpler and allws access to globals. can be changed if needed. + mut file := parse_comptime(tmpl_path, v_code, p.table, p.pref, p.scope) file.path = tmpl_path - // copy vars from current fn scope into vweb_tmpl scope - for mut stmt in file.stmts { - if mut stmt is ast.FnDecl { - if stmt.name == 'main.vweb_tmpl_$tmp_fn_name' { - for _, mut obj in p.scope.objects { - if mut obj is ast.Var { - stmt.scope.register(ast.Var{ - ...obj - is_used: true - pos: stmt.body_pos - }) - } - } - break - } - } - } return ast.ComptimeCall{ scope: 0 is_vweb: true diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index f19384aca8..91afa31779 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -115,11 +115,12 @@ pub fn parse_stmt(text string, table &ast.Table, scope &ast.Scope) ast.Stmt { return p.stmt(false) } -pub fn parse_comptime(text string, table &ast.Table, pref &pref.Preferences, scope &ast.Scope) &ast.File { +pub fn parse_comptime(tmpl_path string, text string, table &ast.Table, pref &pref.Preferences, scope &ast.Scope) &ast.File { $if trace_parse_comptime ? { eprintln('> ${@MOD}.${@FN} text: $text') } mut p := Parser{ + file_name: tmpl_path scanner: scanner.new_scanner(text, .skip_comments, pref) table: table pref: pref diff --git a/vlib/v/tests/comptime_call_tmpl_variable_scope_test.tpl b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.tpl new file mode 100644 index 0000000000..742feb74b3 --- /dev/null +++ b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.tpl @@ -0,0 +1,2 @@ +$a.name +$b \ No newline at end of file diff --git a/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v new file mode 100644 index 0000000000..18db0e9a2b --- /dev/null +++ b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v @@ -0,0 +1,11 @@ +[heap] +struct MyHeapStruct { + name string +} + +// make sure dereferencing of heap stucts works in selector expr (in tmpl), +fn test_heap_struct_dereferencing_in_selector_expr() { + a := MyHeapStruct{name: 'my_heap_struct_a'} + b := 2 + $tmpl('comptime_call_tmpl_variable_scope_test.tpl') +} From e1c8b07fa5ee8bc412f8b23fde13999dbba455c6 Mon Sep 17 00:00:00 2001 From: Joe Conigliaro Date: Fri, 8 Apr 2022 17:41:23 +1000 Subject: [PATCH 02/30] checker/tests: format test added in prev commit & rename chekcer prop --- vlib/v/checker/checker.v | 4 ++-- vlib/v/checker/comptime.v | 2 +- vlib/v/tests/comptime_call_tmpl_variable_scope_test.v | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 9ea39054b2..17a8e0429f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -124,7 +124,7 @@ mut: inside_println_arg bool inside_decl_rhs bool inside_if_guard bool // true inside the guard condition of `if x := opt() {}` - vweb_comptime_call_pos int // needed for correctly checking use before decl for vweb templates + comptime_call_pos int // needed for correctly checking use before decl for templates } pub fn new_checker(table &ast.Table, pref &pref.Preferences) &Checker { @@ -3109,7 +3109,7 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type { // if the variable is declared before the comptime call then we can assume all is well. // `node.name !in node.scope.objects` checks it's an inherited var (not defined in the tmpl). node_pos := if c.pref.is_vweb && node.name !in node.scope.objects { - c.vweb_comptime_call_pos + c.comptime_call_pos } else { node.pos.pos } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index aff684ef00..49db3f5265 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -36,7 +36,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { is_vweb: true } mut c2 := new_checker(c.table, pref2) - c2.vweb_comptime_call_pos = node.pos.pos + c2.comptime_call_pos = node.pos.pos c2.check(node.vweb_tmpl) c.warnings << c2.warnings c.errors << c2.errors diff --git a/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v index 18db0e9a2b..90f9bf3b4b 100644 --- a/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v +++ b/vlib/v/tests/comptime_call_tmpl_variable_scope_test.v @@ -3,9 +3,11 @@ struct MyHeapStruct { name string } -// make sure dereferencing of heap stucts works in selector expr (in tmpl), +// make sure dereferencing of heap stucts works in selector expr (in tmpl), fn test_heap_struct_dereferencing_in_selector_expr() { - a := MyHeapStruct{name: 'my_heap_struct_a'} + a := MyHeapStruct{ + name: 'my_heap_struct_a' + } b := 2 $tmpl('comptime_call_tmpl_variable_scope_test.tpl') } From 426e9d1734676bdf9e554ee1b1cc323a224095fd Mon Sep 17 00:00:00 2001 From: Joe Conigliaro Date: Fri, 8 Apr 2022 18:25:08 +1000 Subject: [PATCH 03/30] checker: fix condition for checking if tmpl var is inherited --- vlib/v/checker/checker.v | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 17a8e0429f..2687a3d858 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3107,14 +3107,15 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type { ast.Var { // inside vweb tmpl ident positions are meaningless, use the position of the comptime call. // if the variable is declared before the comptime call then we can assume all is well. - // `node.name !in node.scope.objects` checks it's an inherited var (not defined in the tmpl). - node_pos := if c.pref.is_vweb && node.name !in node.scope.objects { + // `node.name !in node.scope.objects && node.scope.start_pos < c.comptime_call_pos` (inherited) + node_pos := if c.pref.is_vweb && node.name !in node.scope.objects + && node.scope.start_pos < c.comptime_call_pos { c.comptime_call_pos } else { node.pos.pos } if node_pos < obj.pos.pos { - c.error('undefined variable `$node.name` (used before declaration)', + c.error('undefined variable `$node.name` (used before declaration) # $node_pos < $obj.pos.pos | $node.pos.pos', node.pos) } is_sum_type_cast := obj.smartcasts.len != 0 From a0c07454b1f128ace2e6fd5a38bb82f2b254909e Mon Sep 17 00:00:00 2001 From: Joe Conigliaro Date: Fri, 8 Apr 2022 18:27:14 +1000 Subject: [PATCH 04/30] checker: remove debug code from previous commit --- vlib/v/checker/checker.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2687a3d858..4a1716f917 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3115,7 +3115,7 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type { node.pos.pos } if node_pos < obj.pos.pos { - c.error('undefined variable `$node.name` (used before declaration) # $node_pos < $obj.pos.pos | $node.pos.pos', + c.error('undefined variable `$node.name` (used before declaration)', node.pos) } is_sum_type_cast := obj.smartcasts.len != 0 From 52f1c615a6785295da22fce91b61cac23a809d64 Mon Sep 17 00:00:00 2001 From: Larpon Date: Fri, 8 Apr 2022 12:22:23 +0200 Subject: [PATCH 05/30] fontstash: document all functions (#13969) --- vlib/fontstash/fontstash.c.v | 114 +++++++++++++++--- ...{fontstash_structs.v => fontstash_enums.v} | 0 2 files changed, 100 insertions(+), 14 deletions(-) rename vlib/fontstash/{fontstash_structs.v => fontstash_enums.v} (100%) diff --git a/vlib/fontstash/fontstash.c.v b/vlib/fontstash/fontstash.c.v index a9e35ee881..1f546acf34 100644 --- a/vlib/fontstash/fontstash.c.v +++ b/vlib/fontstash/fontstash.c.v @@ -24,25 +24,34 @@ pub type Context = C.FONScontext pub const ( // TODO: fontstash.used_import is used to keep v from warning about unused imports used_import = 1 + invalid = C.FONS_INVALID // -1 ) -// Contructor and destructor. +// create_internal returns a fontstash Context allocated on the heap. +// +// See also: delete_internal [inline] pub fn create_internal(params &C.FONSparams) &Context { return C.fonsCreateInternal(params) } +// delete_internal deletes and free memory of `s` fontstash Context. +// +// See also: create_internal [inline] pub fn delete_internal(s &Context) { C.fonsDeleteInternal(s) } +// set_error_callback sets `callback` as a function to be called if fontstash +// encounter any errors. `uptr` can be used to pass custom userdata. [inline] pub fn (s &Context) set_error_callback(callback fn (voidptr, int, int), uptr voidptr) { C.fonsSetErrorCallback(s, callback, uptr) } -// Returns current atlas size. +// get_atlas_size returns the current size of the texture atlas which +// the font is rendered to. [inline] pub fn (s &Context) get_atlas_size() (int, int) { mut width := 0 @@ -51,126 +60,203 @@ pub fn (s &Context) get_atlas_size() (int, int) { return width, height } -// Expands the atlas size. +// expand_atlas expands the font texture atlas size to `width` x `height`. [inline] pub fn (s &Context) expand_atlas(width int, height int) int { return C.fonsExpandAtlas(s, width, height) } -// Resets the whole stash. +// reset_atlas resets `width` x `height` of the font texture atlas. [inline] pub fn (s &Context) reset_atlas(width int, height int) int { return C.fonsResetAtlas(s, width, height) } -// Add fonts +// get_font_by_name returns the id of the font with `name` or +// `fontstash.invalid` if no font with `name` could be found. [inline] pub fn (s &Context) get_font_by_name(name string) int { return C.fonsGetFontByName(s, &char(name.str)) } +// add_fallback_font adds a fallback font to the `base` font id in the Context. +// `fallback` is expected to be the id of a previous, successfully, added font. +// add_fallback_font returns `1` on success, `0` otherwise. [inline] pub fn (s &Context) add_fallback_font(base int, fallback int) int { return C.fonsAddFallbackFont(s, base, fallback) } +// add_font_mem adds the font data located in memory to the Context. +// `name` is the human readable name for the font. +// `free_data` indicates if `data` should be freed after the font is added. +// The function returns the id of the font on success, `fontstash.invalid` otherwise. [inline] pub fn (s &Context) add_font_mem(name string, data []byte, free_data bool) int { return C.fonsAddFontMem(s, &char(name.str), data.data, data.len, int(free_data)) } -// State handling +// push_state pushes a new state on the state stack. +// A state holds the current attributes of the rendering, +// attributes are things like color, size, the font in use, blur effect etc. +// +// See also: pop_state +// See also: clear_state +// See also: set_size +// See also: set_color +// See also: set_spacing +// See also: set_blur +// See also: set_align +// See also: set_font [inline] pub fn (s &Context) push_state() { C.fonsPushState(s) } +// pop_state pops the current state from the state stack. +// +// See also: push_state +// See also: clear_state [inline] pub fn (s &Context) pop_state() { C.fonsPopState(s) } +// clear_state clears the current state. +// +// See also: push_state +// See also: pop_state [inline] pub fn (s &Context) clear_state() { C.fonsClearState(s) } -// State setting +// set_size sets the font size to `size` on the active state. +// +// See also: push_state +// See also: pop_state +// See also: clear_state [inline] pub fn (s &Context) set_size(size f32) { C.fonsSetSize(s, size) } +// set_color sets the font color to `color` on the active state. +// +// See also: push_state +// See also: pop_state +// See also: clear_state [inline] pub fn (s &Context) set_color(color u32) { C.fonsSetColor(s, color) } +// set_spacing sets the font spacing to `spacing` on the active state. +// +// See also: push_state +// See also: pop_state +// See also: clear_state [inline] pub fn (s &Context) set_spacing(spacing f32) { C.fonsSetSpacing(s, spacing) } +// set_blur sets the font blur effect to `blur` on the active state. +// +// See also: push_state +// See also: pop_state +// See also: clear_state [inline] pub fn (s &Context) set_blur(blur f32) { C.fonsSetBlur(s, blur) } +// set_align sets the font aligning to `align` on the active state. +// +// See also: push_state +// See also: pop_state +// See also: clear_state [inline] pub fn (s &Context) set_align(align int) { C.fonsSetAlign(s, align) } +// set_font sets the font used for this render on the active state. +// `font_id` is the id of the loaded font. +// +// See also: push_state +// See also: pop_state +// See also: clear_state [inline] -pub fn (s &Context) set_font(font int) { - C.fonsSetFont(s, font) +pub fn (s &Context) set_font(font_id int) { + C.fonsSetFont(s, font_id) } -// Draw text +// draw_text draws the `text` string at position `x`,`y`. +// The function returns the `x` coordinate of the resulting render. [inline] pub fn (s &Context) draw_text(x f32, y f32, text string) f32 { return C.fonsDrawText(s, x, y, &char(text.str), &char(0)) } -// Measure text +// text_bounds fills the `bounds` argument with the pixel dimensions +// of the rendered `text` at position `x`,`y`. +// +// `bounds` is expected to be of type `mut bounds := [4]f32{}`. +// Call example: `ctx.text_bounds(0, 0, 'example', &bounds[0])`. +// `bounds[0]` is the `x` coordinate of the top-left point. +// `bounds[1]` is the `y` coordinate of the top-left point. +// `bounds[2]` is the `x` coordinate of the bottom-right point. +// `bounds[3]` is the `y` coordinate of the bottom-right point. [inline] pub fn (s &Context) text_bounds(x f32, y f32, text string, bounds &f32) f32 { return C.fonsTextBounds(s, x, y, &char(text.str), &char(0), bounds) } +// line_bounds fills `miny` and `maxy` with the values of the `minimum` +// and `maximum` line bounds respectively. [inline] pub fn (s &Context) line_bounds(y f32, miny &f32, maxy &f32) { C.fonsLineBounds(s, y, miny, maxy) } +// vert_metrics assigns the respective values of `ascender`, `descender` and `lineh`. [inline] pub fn (s &Context) vert_metrics(ascender &f32, descender &f32, lineh &f32) { C.fonsVertMetrics(s, ascender, descender, lineh) } -// Text iterator +// text_iter_init initalizes the text iterator `iter`. [inline] pub fn (s &Context) text_iter_init(iter &C.FONStextIter, x f32, y f32, str &char, end &char) int { return C.fonsTextIterInit(s, iter, x, y, str, end) } +// text_iter_next advances `iter` to the next `quad`. [inline] pub fn (s &Context) text_iter_next(iter &C.FONStextIter, quad &C.FONSquad) int { return C.fonsTextIterNext(s, iter, quad) } -// Pull texture changes +// get_texture_data returns the current Context's raw texture data. +// `width` and `height` is assigned the size of the texture dimensions. [inline] pub fn (s &Context) get_texture_data(width &int, height &int) &byte { return &byte(C.fonsGetTextureData(s, width, height)) } +// validate_texture fills the `dirty` argument with the pixel dimensions +// of the dirty rectangle of the Context's raw texture, if any. +// +// `dirty` is expected to be of type `mut dirty := [4]int{}`. +// Call example: `is_dirty := ctx.validate_texture(&dirty[0])`. +// The function returns `1` if the texture has a dirty rectangle, `0` otherwise. [inline] pub fn (s &Context) validate_texture(dirty &int) int { return C.fonsValidateTexture(s, dirty) } -// Draws the stash texture for debugging +// draw_debug draws the stash texture for debugging. [inline] pub fn (s &Context) draw_debug(x f32, y f32) { C.fonsDrawDebug(s, x, y) diff --git a/vlib/fontstash/fontstash_structs.v b/vlib/fontstash/fontstash_enums.v similarity index 100% rename from vlib/fontstash/fontstash_structs.v rename to vlib/fontstash/fontstash_enums.v From 45a427e68bead369c9da49d6424a331f684f3138 Mon Sep 17 00:00:00 2001 From: Larpon Date: Fri, 8 Apr 2022 12:32:38 +0200 Subject: [PATCH 06/30] clipboard: fix segfault when pasting to some X11 apps. Fixes #13891 (#13971) --- vlib/clipboard/x11/clipboard.c.v | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/vlib/clipboard/x11/clipboard.c.v b/vlib/clipboard/x11/clipboard.c.v index 85e022b595..ff96bdf67b 100644 --- a/vlib/clipboard/x11/clipboard.c.v +++ b/vlib/clipboard/x11/clipboard.c.v @@ -330,7 +330,7 @@ fn (mut cb Clipboard) start_listener() { property: xsre.property } if !cb.transmit_selection(&xse) { - xse.property = new_atom(0) + xse.property = Atom(0) } C.XSendEvent(cb.display, xse.requestor, 0, C.PropertyChangeMask, voidptr(&xse)) C.XFlush(cb.display) @@ -479,10 +479,6 @@ fn (cb &Clipboard) get_supported_targets() []Atom { return cb.get_atoms(AtomType.utf8_string, .xa_string, .text, .text_plain, .text_html) } -fn new_atom(value int) &Atom { - return unsafe { &Atom(&u64(u64(value))) } -} - fn create_xwindow(display &C.Display) Window { n := C.DefaultScreen(display) return C.XCreateSimpleWindow(display, C.RootWindow(display, n), 0, 0, 1, 1, 0, C.BlackPixel(display, From 617608b23d884269fc5a28b898c047a85067e348 Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 8 Apr 2022 19:51:37 +0800 Subject: [PATCH 07/30] cgen: fix optional struct declarations for empty structs (#13970) --- vlib/json/json_decode_with_optional_arg_test.v | 4 +--- vlib/v/gen/c/cgen.v | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/vlib/json/json_decode_with_optional_arg_test.v b/vlib/json/json_decode_with_optional_arg_test.v index 3aba409bf1..97b4932b8b 100644 --- a/vlib/json/json_decode_with_optional_arg_test.v +++ b/vlib/json/json_decode_with_optional_arg_test.v @@ -1,9 +1,7 @@ import json import os -struct DbConfig { - foo int -} +struct DbConfig {} fn test_json_decode_with_optional_arg() { if ret := print_info() { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 7cc472feb7..6b2006d474 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -972,7 +972,7 @@ fn (g Gen) optional_type_text(styp string, base string) string { ret := 'struct $styp { byte state; IError err; - byte data[sizeof($size)]; + byte data[sizeof($size) > 0 ? sizeof($size) : 1]; }' return ret } From 2a88b313d43be20483f3bd4af1aa86a33ae1aa43 Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 8 Apr 2022 22:27:05 +0800 Subject: [PATCH 08/30] cgen: format json encode/decode generated c codes (#13972) --- vlib/v/gen/c/fn.v | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 3bbd65a198..a7d6ce99a3 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1206,6 +1206,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { // `json__encode` => `json__encode_User` // encode_name := c_name(name) + '_' + util.no_dots(json_type_str) encode_name := js_enc_name(json_type_str) + g.empty_line = true g.writeln('// json.encode') g.write('cJSON* $json_obj = ${encode_name}(') if node.args[0].typ.is_ptr() { @@ -1225,6 +1226,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { typ := c_name(g.typ(ast_type.typ)) fn_name := c_name(name) + '_' + typ g.gen_json_for_type(ast_type.typ) + g.empty_line = true g.writeln('// json.decode') g.write('cJSON* $json_obj = json__json_parse(') // Skip the first argument in json.decode which is a type @@ -1233,10 +1235,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { g.call_args(node) g.writeln(');') tmp2 = g.new_tmp_var() - g.writeln('Option_$typ $tmp2 = $fn_name ($json_obj);') + g.writeln('Option_$typ $tmp2 = ${fn_name}($json_obj);') } if !g.is_autofree { - g.write('cJSON_Delete($json_obj); //del') + g.write('cJSON_Delete($json_obj); // del') } g.write('\n$cur_line') name = '' From 2d867a27662b28387f0281b01bf9623f275c7b0f Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 8 Apr 2022 16:38:34 +0200 Subject: [PATCH 09/30] ast: fix reported errors, when arrays of types defined in modules are involved (#13952) --- vlib/v/ast/types.v | 7 +++++ .../tests/check_err_msg_with_generics.out | 27 +++++++++++++++++++ .../tests/check_err_msg_with_generics.vv | 16 +++++++++++ 3 files changed, 50 insertions(+) create mode 100644 vlib/v/checker/tests/check_err_msg_with_generics.out create mode 100644 vlib/v/checker/tests/check_err_msg_with_generics.vv diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 842c304629..7bc360d1c8 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -1155,6 +1155,13 @@ fn (t Table) shorten_user_defined_typenames(originalname string, import_aliases } else if res in import_aliases { res = import_aliases[res] } else { + // FIXME: clean this case and remove the following if + // because it is an hack to format well the type when + // there is a []modul.name + if res.contains('[]') { + idx := res.index('.') or { -1 } + return res[idx + 1..] + } // types defined by the user // mod.submod.submod2.Type => submod2.Type mut parts := res.split('.') diff --git a/vlib/v/checker/tests/check_err_msg_with_generics.out b/vlib/v/checker/tests/check_err_msg_with_generics.out new file mode 100644 index 0000000000..f4587a294a --- /dev/null +++ b/vlib/v/checker/tests/check_err_msg_with_generics.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/check_err_msg_with_generics.vv:15:10: error: cannot cast struct `BSTree>>` to `int` + 13 | fn test_err_msg() { + 14 | typ := datatypes.BSTree>>{} + 15 | println(int(typ)) + | ~~~~~~~~ + 16 | } +vlib/datatypes/bstree.v:190:17: error: cannot append `T` to `[]T` + 188 | } + 189 | bst.in_order_traversal_helper(node.left, mut result) + 190 | result << node.value + | ~~~~~ + 191 | bst.in_order_traversal_helper(node.right, mut result) + 192 | } +vlib/datatypes/bstree.v:210:17: error: cannot append `T` to `[]T` + 208 | bst.post_order_traversal_helper(node.left, mut result) + 209 | bst.post_order_traversal_helper(node.right, mut result) + 210 | result << node.value + | ~~~~~ + 211 | } + 212 | +vlib/datatypes/bstree.v:226:17: error: cannot append `T` to `[]T` + 224 | return + 225 | } + 226 | result << node.value + | ~~~~~ + 227 | bst.pre_order_traversal_helper(node.left, mut result) + 228 | bst.pre_order_traversal_helper(node.right, mut result) diff --git a/vlib/v/checker/tests/check_err_msg_with_generics.vv b/vlib/v/checker/tests/check_err_msg_with_generics.vv new file mode 100644 index 0000000000..57da5432de --- /dev/null +++ b/vlib/v/checker/tests/check_err_msg_with_generics.vv @@ -0,0 +1,16 @@ +import datatypes + +type Result = Err | Ok + +struct Ok { + value T +} + +struct Err { + value U +} + +fn test_err_msg() { + typ := datatypes.BSTree>>{} + println(int(typ)) +} From 60e718e7c611978bd664cc83b7ec8b335b802f28 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 9 Apr 2022 13:03:52 +0300 Subject: [PATCH 10/30] test: save removing the nonexistent binary output for v fmt and v vet tests --- cmd/tools/modules/testing/common.v | 22 +++++++++++----------- cmd/tools/vfmt.v | 8 ++------ vlib/os/os_nix.c.v | 5 ++++- vlib/term/term.v | 4 ++-- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/cmd/tools/modules/testing/common.v b/cmd/tools/modules/testing/common.v index 90063733e7..93d90c1e4d 100644 --- a/cmd/tools/modules/testing/common.v +++ b/cmd/tools/modules/testing/common.v @@ -306,12 +306,14 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr { mut run_js := false is_fmt := ts.vargs.contains('fmt') + is_vet := ts.vargs.contains('vet') + produces_file_output := !(is_fmt || is_vet) if relative_file.ends_with('js.v') { - if !is_fmt { + if produces_file_output { cmd_options << ' -b js' + run_js = true } - run_js = true } if relative_file.contains('global') && !is_fmt { @@ -333,13 +335,13 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr { fname.replace('.v', '') } generated_binary_fpath := os.join_path_single(tmpd, generated_binary_fname) - if os.exists(generated_binary_fpath) { - if ts.rm_binaries { - os.rm(generated_binary_fpath) or {} + if produces_file_output { + if os.exists(generated_binary_fpath) { + if ts.rm_binaries { + os.rm(generated_binary_fpath) or {} + } } - } - if !ts.vargs.contains('fmt') { cmd_options << ' -o ${os.quoted_path(generated_binary_fpath)}' } cmd := '${os.quoted_path(ts.vexe)} ' + cmd_options.join(' ') + ' ${os.quoted_path(file)}' @@ -421,10 +423,8 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr { } } } - if os.exists(generated_binary_fpath) { - if ts.rm_binaries { - os.rm(generated_binary_fpath) or {} - } + if produces_file_output && os.exists(generated_binary_fpath) && ts.rm_binaries { + os.rm(generated_binary_fpath) or {} } return pool.no_result } diff --git a/cmd/tools/vfmt.v b/cmd/tools/vfmt.v index 7412bfd21b..fbf22de893 100644 --- a/cmd/tools/vfmt.v +++ b/cmd/tools/vfmt.v @@ -247,12 +247,8 @@ fn (mut foptions FormatOptions) post_process_file(file string, formatted_file_pa 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 + println("$file is not vfmt'ed") + return error('') } if foptions.is_c { if is_formatted_different { diff --git a/vlib/os/os_nix.c.v b/vlib/os/os_nix.c.v index 9d855f34c6..ae8de8ddc0 100644 --- a/vlib/os/os_nix.c.v +++ b/vlib/os/os_nix.c.v @@ -332,7 +332,10 @@ pub fn execute(cmd string) Result { // if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') { // return Result{ exit_code: -1, output: ';, &&, || and \\n are not allowed in shell commands' } // } - pcmd := if cmd.contains('2>') { cmd } else { '$cmd 2>&1' } + pcmd := if cmd.contains('2>') { cmd.clone() } else { '$cmd 2>&1' } + defer { + unsafe { pcmd.free() } + } f := vpopen(pcmd) if isnil(f) { return Result{ diff --git a/vlib/term/term.v b/vlib/term/term.v index 3cf60fef26..646e559775 100644 --- a/vlib/term/term.v +++ b/vlib/term/term.v @@ -142,11 +142,11 @@ pub fn h_divider(divider string) string { // ==== TITLE ========================= pub fn header_left(text string, divider string) string { plain_text := strip_ansi(text) - xcols, _ := get_terminal_size() + xcols, _ := get_terminal_size() // can get 0 in lldb/gdb cols := imax(1, xcols) relement := if divider.len > 0 { divider } else { ' ' } hstart := relement.repeat(4)[0..4] - remaining_cols := (cols - (hstart.len + 1 + plain_text.len + 1)) + remaining_cols := imax(0, (cols - (hstart.len + 1 + plain_text.len + 1))) hend := relement.repeat((remaining_cols + 1) / relement.len)[0..remaining_cols] return '$hstart $text $hend' } From e3da3101f6ba8abc9fc523426e29f17e66a4026e Mon Sep 17 00:00:00 2001 From: pancake Date: Sat, 9 Apr 2022 13:26:01 +0200 Subject: [PATCH 11/30] vpm: simplify output for `v list` (#13975) --- cmd/tools/vpm.v | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/tools/vpm.v b/cmd/tools/vpm.v index bdc4645519..f99187d32b 100644 --- a/cmd/tools/vpm.v +++ b/cmd/tools/vpm.v @@ -445,12 +445,11 @@ fn vpm_outdated() { fn vpm_list() { module_names := get_installed_modules() if module_names.len == 0 { - println('You have no modules installed.') + eprintln('You have no modules installed.') exit(0) } - println('Installed modules:') for mod in module_names { - println(' $mod') + println(mod) } } From 804f2f56d48bd3637944c1edfbe5d24911ce7a35 Mon Sep 17 00:00:00 2001 From: pancake Date: Sat, 9 Apr 2022 13:29:41 +0200 Subject: [PATCH 12/30] vpm: support `v install -once module_name`(#13977) --- cmd/tools/vpm.v | 36 +++++++++++++++++++++++++++--------- cmd/v/help/install.txt | 1 + doc/docs.md | 8 +++++++- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/cmd/tools/vpm.v b/cmd/tools/vpm.v index f99187d32b..260a24e283 100644 --- a/cmd/tools/vpm.v +++ b/cmd/tools/vpm.v @@ -87,6 +87,12 @@ fn main() { module_names = manifest.dependencies } mut source := Source.vpm + if '--once' in options { + module_names = vpm_once_filter(module_names) + if module_names.len == 0 { + return + } + } if '--git' in options { source = Source.git } @@ -327,6 +333,17 @@ fn vpm_install_from_vcs(module_names []string, vcs_key string) { } } +fn vpm_once_filter(module_names []string) []string { + installed_modules := get_installed_modules() + mut toinstall := []string{} + for mn in module_names { + if mn !in installed_modules { + toinstall << mn + } + } + return toinstall +} + fn vpm_install(module_names []string, source Source) { if settings.is_help { vhelp.show_topic('install') @@ -336,15 +353,16 @@ fn vpm_install(module_names []string, source Source) { println('´v install´ requires *at least one* module name.') exit(2) } - - if source == .vpm { - vpm_install_from_vpm(module_names) - } - if source == .git { - vpm_install_from_vcs(module_names, 'git') - } - if source == .hg { - vpm_install_from_vcs(module_names, 'hg') + match source { + .vpm { + vpm_install_from_vpm(module_names) + } + .git { + vpm_install_from_vcs(module_names, 'git') + } + .hg { + vpm_install_from_vcs(module_names, 'hg') + } } } diff --git a/cmd/v/help/install.txt b/cmd/v/help/install.txt index ba0b3cb175..975a690d7f 100644 --- a/cmd/v/help/install.txt +++ b/cmd/v/help/install.txt @@ -8,6 +8,7 @@ Options: --vpm - [Default] Install from vpm --git - Install from git repository url --hg - Install from mercurial repository url + --once - Only install the module if it was not previously installed -help - Show usage info. -v - Print more details about the performed operation. -server-url - When doing network operations, use this vpm server. Can be given multiple times. diff --git a/doc/docs.md b/doc/docs.md index c0c3459f1d..4bd8afe535 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -4601,13 +4601,19 @@ v install ui Modules can be installed directly from git or mercurial repositories. ```powershell -v install [--git|--hg] [url] +v install [--once] [--git|--hg] [url] ``` **Example:** ```powershell v install --git https://github.com/vlang/markdown ``` +Sometimes you may want to install the dependencies **ONLY** if those are not installed: + +``` +v install --once [module] +``` + Removing a module with v: ```powershell From bf385d2ac9657b914ae42658aef3bc62a192a878 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sat, 9 Apr 2022 13:53:37 +0100 Subject: [PATCH 13/30] docs: add links to download C compilers (#13984) --- doc/docs.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/docs.md b/doc/docs.md index 4bd8afe535..c07bd51d1b 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -26,6 +26,8 @@ git clone https://github.com/vlang/v cd v make ``` +See [here](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Linux-and-macOS) +for how to install the development tools. ### Windows: You need `git`, and a C compiler like `tcc`, `gcc`, `clang` or `msvc`: @@ -38,6 +40,9 @@ NB: You can also pass one of `-gcc`, `-msvc`, `-clang` to `make.bat` instead, if you do prefer to use a different C compiler, but -tcc is small, fast, and easy to install (V will download a prebuilt binary automatically). +For C compiler downloads and more info, see +[here](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows). + It is recommended to add this folder to the PATH of your environment variables. This can be done with the command `v.exe symlink`. From 704e3c6e7275336b5b4f8da2743871f1ee7d5b25 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sat, 9 Apr 2022 20:57:27 +0800 Subject: [PATCH 14/30] cgen: fix error for fn with fixed array argument (fix #13976) (#13982) --- vlib/v/gen/c/assign.v | 7 ++-- vlib/v/gen/c/infix_expr.v | 3 +- vlib/v/tests/fn_with_fixed_array_args_test.v | 44 ++++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 vlib/v/tests/fn_with_fixed_array_args_test.v diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index ff7825263e..a167ffbbc1 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -238,11 +238,12 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) { } else { g.out.go_back_to(pos) is_var_mut := !is_decl && left.is_auto_deref_var() - addr := if is_var_mut { '' } else { '&' } + addr_left := if is_var_mut { '' } else { '&' } g.writeln('') - g.write('memcpy($addr') + g.write('memcpy($addr_left') g.expr(left) - g.writeln(', &$v_var, sizeof($arr_typ));') + addr_val := if is_fixed_array_var { '' } else { '&' } + g.writeln(', $addr_val$v_var, sizeof($arr_typ));') } g.is_assign_lhs = false } else { diff --git a/vlib/v/gen/c/infix_expr.v b/vlib/v/gen/c/infix_expr.v index 04140453e2..b7e4b76a2a 100644 --- a/vlib/v/gen/c/infix_expr.v +++ b/vlib/v/gen/c/infix_expr.v @@ -626,7 +626,8 @@ fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) { if elem_sym.kind == .function { g.write(', _MOV((voidptr[]){ ') } else if elem_is_array_var { - g.write(', &') + addr := if elem_sym.kind == .array_fixed { '' } else { '&' } + g.write(', $addr') } else { g.write(', _MOV(($elem_type_str[]){ ') } diff --git a/vlib/v/tests/fn_with_fixed_array_args_test.v b/vlib/v/tests/fn_with_fixed_array_args_test.v new file mode 100644 index 0000000000..e13996b290 --- /dev/null +++ b/vlib/v/tests/fn_with_fixed_array_args_test.v @@ -0,0 +1,44 @@ +struct Test1 { +mut: + value [4]int +} + +fn (mut t Test1) set(new_value [4]int) { + t.value = new_value +} + +fn test_fn_with_fixed_array_argument_1() { + mut t := Test1{} + + println(t) + assert '$t.value' == '[0, 0, 0, 0]' + + t.set([1, 2, 3, 4]!) + + println(t) + assert '$t.value' == '[1, 2, 3, 4]' +} + +struct Test2 { +mut: + fixed_value [2][4]int + dynamic_value [][4]int +} + +fn (mut t Test2) set(index int, new_value [4]int) { + t.fixed_value[index] = new_value + t.dynamic_value << new_value +} + +fn test_fn_with_fixed_array_argument_2() { + mut t := Test2{} + + println(t) + assert '$t.fixed_value' == '[[0, 0, 0, 0], [0, 0, 0, 0]]' + assert '$t.dynamic_value' == '[]' + + t.set(0, [1, 2, 3, 4]!) + println(t) + assert '$t.fixed_value' == '[[1, 2, 3, 4], [0, 0, 0, 0]]' + assert '$t.dynamic_value' == '[[1, 2, 3, 4]]' +} From 58febe460730c6eabf7e9c4689c372f0bc4a3588 Mon Sep 17 00:00:00 2001 From: Daniel Oberhoff Date: Sat, 9 Apr 2022 16:37:39 +0200 Subject: [PATCH 15/30] cgen: fix autofree of heap variables (#13823) --- vlib/v/gen/c/cgen.v | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 6b2006d474..edcee66730 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -2645,12 +2645,13 @@ fn (mut g Gen) autofree_variable(v ast.Var) { g.autofree_var_call('string_free', v) return } - if sym.has_method('free') { - g.autofree_var_call(free_fn, v) - } else if g.pref.experimental && v.typ.is_ptr() && sym.name.after('.')[0].is_capital() { + if g.pref.experimental && v.typ.is_ptr() && sym.name.after('.')[0].is_capital() { // Free user reference types g.autofree_var_call('free', v) } + if sym.has_method('free') { + g.autofree_var_call(free_fn, v) + } } fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) { @@ -2698,7 +2699,11 @@ fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) { if v.typ == ast.error_type && !v.is_autofree_tmp { return } - af.writeln('\t${free_fn_name}(&${c_name(v.name)}); // autofreed var $g.cur_mod.name $g.is_builtin_mod') + if v.is_auto_heap { + af.writeln('\t${free_fn_name}(${c_name(v.name)}); // autofreed heap var $g.cur_mod.name $g.is_builtin_mod') + } else { + af.writeln('\t${free_fn_name}(&${c_name(v.name)}); // autofreed var $g.cur_mod.name $g.is_builtin_mod') + } } g.autofree_scope_stmts << af.str() } From df30b79971eb201968a21604f3bb1c75ab7103cd Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 9 Apr 2022 19:50:03 +0300 Subject: [PATCH 16/30] checker: allow for `arr.any(opt_fn()?)`, add test --- vlib/v/checker/fn.v | 4 ++++ vlib/v/tests/array_methods_test.v | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index c473e9fc90..9056b7a41a 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1726,6 +1726,10 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, node ast if is_map && arg_expr.return_type in [ast.void_type, 0] { c.error('type mismatch, `$arg_expr.name` does not return anything', arg_expr.pos) } else if !is_map && arg_expr.return_type != ast.bool_type { + if arg_expr.or_block.kind != .absent && arg_expr.return_type.has_flag(.optional) + && arg_expr.return_type.clear_flag(.optional) == ast.bool_type { + return + } c.error('type mismatch, `$arg_expr.name` must return a bool', arg_expr.pos) } } diff --git a/vlib/v/tests/array_methods_test.v b/vlib/v/tests/array_methods_test.v index f6dce7cdf6..d36017f2ae 100644 --- a/vlib/v/tests/array_methods_test.v +++ b/vlib/v/tests/array_methods_test.v @@ -30,3 +30,12 @@ fn test_array_eval_count() { a4 = Counter{} assert a4.new_arr('all() failed').all(it == 2) == false } + +fn opt_bool_fn() ?bool { + return true +} + +fn test_any_called_with_opt_bool_fn() ? { + _ := [1, 2, 3].any(opt_bool_fn() ?) + assert true +} From 89d64b21ead80700a947a4bf0910698287886f8d Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 9 Apr 2022 20:06:27 +0300 Subject: [PATCH 17/30] ci: remove manual free(f) in an autofree test, after 58febe46, which does it automatically now --- vlib/v/tests/valgrind/free_heap_foos.v | 2 -- 1 file changed, 2 deletions(-) diff --git a/vlib/v/tests/valgrind/free_heap_foos.v b/vlib/v/tests/valgrind/free_heap_foos.v index 2d7971bd28..0d0c35bafc 100644 --- a/vlib/v/tests/valgrind/free_heap_foos.v +++ b/vlib/v/tests/valgrind/free_heap_foos.v @@ -16,8 +16,6 @@ fn create(x int) &Foo { fn (f &Foo) free() { println('> freeing Foo $f.x at address: ${voidptr(f)} | frees.len: $frees.len') frees << f.x - // TODO: this should NOT be necessary - the compiler should do it automatically in the parent scope - unsafe { free(f) } } fn create_some_foos() { From 473bc0254d9997daa5824e70a8ea4e7642ecbee3 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 10 Apr 2022 02:50:34 +0800 Subject: [PATCH 18/30] checker: check error for array of generic struct init (#13987) --- vlib/v/checker/containers.v | 16 ++++++++++++++++ .../tests/array_of_generic_struct_init_err.out | 6 ++++++ .../tests/array_of_generic_struct_init_err.vv | 7 +++++++ 3 files changed, 29 insertions(+) create mode 100644 vlib/v/checker/tests/array_of_generic_struct_init_err.out create mode 100644 vlib/v/checker/tests/array_of_generic_struct_init_err.vv diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index 6647998a19..bee64cdb80 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -9,6 +9,22 @@ pub fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { mut elem_type := ast.void_type // []string - was set in parser if node.typ != ast.void_type { + if node.elem_type != 0 { + elem_sym := c.table.sym(node.elem_type) + if elem_sym.kind == .struct_ { + elem_info := elem_sym.info as ast.Struct + if elem_info.generic_types.len > 0 && elem_info.concrete_types.len == 0 + && !node.elem_type.has_flag(.generic) { + if c.table.cur_concrete_types.len == 0 { + c.error('generic struct must specify type parameter, e.g. Foo', + node.elem_type_pos) + } else { + c.error('generic struct must specify type parameter, e.g. Foo', + node.elem_type_pos) + } + } + } + } if node.exprs.len == 0 { if node.has_cap { c.check_array_init_para_type('cap', node.cap_expr, node.pos) diff --git a/vlib/v/checker/tests/array_of_generic_struct_init_err.out b/vlib/v/checker/tests/array_of_generic_struct_init_err.out new file mode 100644 index 0000000000..b24df44194 --- /dev/null +++ b/vlib/v/checker/tests/array_of_generic_struct_init_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/array_of_generic_struct_init_err.vv:6:15: error: generic struct must specify type parameter, e.g. Foo + 4 | + 5 | fn main() { + 6 | mut arr := []Item{} + | ~~~~ + 7 | } diff --git a/vlib/v/checker/tests/array_of_generic_struct_init_err.vv b/vlib/v/checker/tests/array_of_generic_struct_init_err.vv new file mode 100644 index 0000000000..4f36203185 --- /dev/null +++ b/vlib/v/checker/tests/array_of_generic_struct_init_err.vv @@ -0,0 +1,7 @@ +struct Item{ + val T +} + +fn main() { + mut arr := []Item{} +} From 3571f66a82fb3acc904444f37977d455aa02e11d Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 10 Apr 2022 09:21:58 +0200 Subject: [PATCH 19/30] pref: fix access in invalid position on `v search ''` (#13993) --- vlib/v/pref/pref.v | 4 ++-- vlib/v/pref/pref_test.v | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 vlib/v/pref/pref_test.v diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 8ce75fcb51..c0e9736c66 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -110,7 +110,7 @@ pub mut: test_runner string // can be 'simple' (fastest, but much less detailed), 'tap', 'normal' profile_file string // the profile results will be stored inside profile_file profile_no_inline bool // when true, [inline] functions would not be profiled - profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on. + profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on. translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc is_prod bool // use "-O2" obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" @@ -634,7 +634,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin eprintln('Use `v $arg` instead.') exit(1) } - if arg[0] == `-` { + if arg.len != 0 && arg[0] == `-` { if arg[1..] in pref.list_of_flags_with_param { // skip parameter i++ diff --git a/vlib/v/pref/pref_test.v b/vlib/v/pref/pref_test.v new file mode 100644 index 0000000000..5ce05dfc4f --- /dev/null +++ b/vlib/v/pref/pref_test.v @@ -0,0 +1,8 @@ +module pref + +fn test_check_parametes() { + // reproducing issue https://github.com/vlang/v/issues/13983 + _, cmd := parse_args_and_show_errors(['help'], [''], true) + // no command found from args + assert cmd == '' +} From 93a5d031829fd27d03c23f17dccc0d446e64612e Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 10 Apr 2022 15:24:36 +0800 Subject: [PATCH 20/30] parser: improve embedded struct parsing/better error for `Architecture []string` (#13995) --- vlib/v/checker/tests/struct_field_name_err.out | 13 +++++++++++++ vlib/v/checker/tests/struct_field_name_err.vv | 9 +++++++++ vlib/v/parser/struct.v | 3 ++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/struct_field_name_err.out create mode 100644 vlib/v/checker/tests/struct_field_name_err.vv diff --git a/vlib/v/checker/tests/struct_field_name_err.out b/vlib/v/checker/tests/struct_field_name_err.out new file mode 100644 index 0000000000..08a4eeb823 --- /dev/null +++ b/vlib/v/checker/tests/struct_field_name_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/struct_field_name_err.vv:2:2: error: field name `Architecture` cannot contain uppercase letters, use snake_case instead + 1 | struct Release { + 2 | Architecture []string + | ~~~~~~~~~~~~~~~~~~~~~ + 3 | Components []string + 4 | } +vlib/v/checker/tests/struct_field_name_err.vv:3:2: error: field name `Components` cannot contain uppercase letters, use snake_case instead + 1 | struct Release { + 2 | Architecture []string + 3 | Components []string + | ~~~~~~~~~~~~~~~~~~~~~ + 4 | } + 5 | diff --git a/vlib/v/checker/tests/struct_field_name_err.vv b/vlib/v/checker/tests/struct_field_name_err.vv new file mode 100644 index 0000000000..b5b33cabc1 --- /dev/null +++ b/vlib/v/checker/tests/struct_field_name_err.vv @@ -0,0 +1,9 @@ +struct Release { + Architecture []string + Components []string +} + +fn main() { + r := Release{} + println(r) +} diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index c1c289b82a..6a7b0af035 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -184,7 +184,8 @@ fn (mut p Parser) struct_decl() ast.StructDecl { p.next() is_field_volatile = true } - is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital()) + is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital() + && (p.peek_tok.kind != .lsbr || p.peek_token(2).kind != .rsbr)) || p.peek_tok.kind == .dot) && language == .v && p.peek_tok.kind != .key_fn is_on_top := ast_fields.len == 0 && !(is_field_mut || is_field_global) mut field_name := '' From 88c4a64a15e413a5cbc946ba6fcfe01472a8e6a9 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 10 Apr 2022 10:39:55 +0300 Subject: [PATCH 21/30] examples: make examples/sokol/drawing.v more symmetrical --- examples/sokol/drawing.v | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/sokol/drawing.v b/examples/sokol/drawing.v index 056908da4c..836ab54338 100644 --- a/examples/sokol/drawing.v +++ b/examples/sokol/drawing.v @@ -17,6 +17,8 @@ fn main() { } title := 'Sokol Drawing Template' desc := sapp.Desc{ + width: 640 + height: 480 user_data: state init_userdata_cb: init frame_userdata_cb: frame @@ -49,9 +51,9 @@ fn draw() { sgl.matrix_mode_projection() sgl.ortho(0.0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0) sgl.c4b(255, 0, 0, 128) - draw_hollow_rect(10, 10, 100, 30) - sgl.c4b(25, 150, 0, 128) - draw_filled_rect(10, 150, 80, 40) + draw_hollow_rect(220, 140, 200, 200) + sgl.c4b(25, 150, 255, 128) + draw_filled_rect(270, 190, 100, 100) // line(0, 0, 500, 500) } From 8517b8f8b01e0463127c58abd67ec2ef93388264 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 10 Apr 2022 11:11:53 +0300 Subject: [PATCH 22/30] examples: use #[] in news_fetcher.v to simplify the code --- examples/news_fetcher.v | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/news_fetcher.v b/examples/news_fetcher.v index 51cd2f986b..e70941c377 100644 --- a/examples/news_fetcher.v +++ b/examples/news_fetcher.v @@ -30,13 +30,10 @@ fn main() { println('failed to fetch data from /v0/topstories.json') return } - mut ids := json.decode([]int, resp.text) or { + ids := json.decode([]int, resp.text) or { println('failed to decode topstories.json') return - } - if ids.len > 10 { - ids = ids[0..10] - } + }#[0..10] mut fetcher_pool := pool.new_pool_processor( callback: worker_fetch ) From 11d9a67e3b361e511fcdc4021970c72266e8821c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E5=BF=83?= Date: Sun, 10 Apr 2022 16:42:17 +0800 Subject: [PATCH 23/30] cgen: fix invalid operands to `an_alias in an_array_of_aliased_values` (#13994) --- vlib/arrays/arrays_test.v | 7 +++++++ vlib/v/gen/c/array.v | 12 ++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/vlib/arrays/arrays_test.v b/vlib/arrays/arrays_test.v index 1d88fcf4c4..74eb69b7d3 100644 --- a/vlib/arrays/arrays_test.v +++ b/vlib/arrays/arrays_test.v @@ -293,3 +293,10 @@ fn test_can_copy_bits() { // map not copyable assert !can_copy_bits() } + +type Str = string + +fn test_alias_string_contains() { + names := [Str('')] + assert (Str('') in names) == true +} diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index 86dd3ea40b..ef32a7e9c4 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -673,7 +673,11 @@ fn (mut g Gen) gen_array_contains_methods() { fn_name := '${left_type_str}_contains' left_info := left_final_sym.info as ast.Array mut elem_type_str := g.typ(left_info.elem_type) - elem_sym := g.table.sym(left_info.elem_type) + mut elem_sym := g.table.sym(left_info.elem_type) + if elem_sym.kind == .alias { + info := elem_sym.info as ast.Alias + elem_sym = g.table.sym(info.parent_type) + } if elem_sym.kind == .function { left_type_str = 'Array_voidptr' elem_type_str = 'voidptr' @@ -751,7 +755,11 @@ fn (mut g Gen) gen_array_index_methods() { fn_name := '${left_type_str}_index' info := final_left_sym.info as ast.Array mut elem_type_str := g.typ(info.elem_type) - elem_sym := g.table.sym(info.elem_type) + mut elem_sym := g.table.sym(info.elem_type) + if elem_sym.kind == .alias { + info_t := elem_sym.info as ast.Alias + elem_sym = g.table.sym(info_t.parent_type) + } if elem_sym.kind == .function { left_type_str = 'Array_voidptr' elem_type_str = 'voidptr' From 3f90809035121fec2ea02640f85bdfe0fd083c99 Mon Sep 17 00:00:00 2001 From: Subhomoy Haldar Date: Sun, 10 Apr 2022 14:13:46 +0530 Subject: [PATCH 24/30] rand: add missing pub modifiers for .byte() and .u32() methods (#13992) --- vlib/rand/musl/musl_rng.v | 4 ++-- vlib/rand/pcg32/pcg32.v | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vlib/rand/musl/musl_rng.v b/vlib/rand/musl/musl_rng.v index 5ae644ae8f..c03f45a919 100644 --- a/vlib/rand/musl/musl_rng.v +++ b/vlib/rand/musl/musl_rng.v @@ -29,7 +29,7 @@ pub fn (mut rng MuslRNG) seed(seed_data []u32) { // byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`. [inline] -fn (mut rng MuslRNG) byte() byte { +pub fn (mut rng MuslRNG) byte() byte { if rng.bytes_left >= 1 { rng.bytes_left -= 1 value := byte(rng.buffer) @@ -70,7 +70,7 @@ fn temper(prev u32) u32 { } // u32 returns a pseudorandom 32-bit unsigned integer (`u32`). -fn (mut rng MuslRNG) u32() u32 { +pub fn (mut rng MuslRNG) u32() u32 { rng.state = rng.state * 1103515245 + 12345 // We are not dividing by 2 (or shifting right by 1) // because we want all 32-bits of random data diff --git a/vlib/rand/pcg32/pcg32.v b/vlib/rand/pcg32/pcg32.v index 4df3ff7b8a..fa1ff41984 100644 --- a/vlib/rand/pcg32/pcg32.v +++ b/vlib/rand/pcg32/pcg32.v @@ -39,7 +39,7 @@ pub fn (mut rng PCG32RNG) seed(seed_data []u32) { // byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`. [inline] -fn (mut rng PCG32RNG) byte() byte { +pub fn (mut rng PCG32RNG) byte() byte { if rng.bytes_left >= 1 { rng.bytes_left -= 1 value := byte(rng.buffer) @@ -70,7 +70,7 @@ pub fn (mut rng PCG32RNG) u16() u16 { // u32 returns a pseudorandom unsigned `u32`. [inline] -fn (mut rng PCG32RNG) u32() u32 { +pub fn (mut rng PCG32RNG) u32() u32 { oldstate := rng.state rng.state = oldstate * (6364136223846793005) + rng.inc xorshifted := u32(((oldstate >> u64(18)) ^ oldstate) >> u64(27)) From 6c25f5b29151d47073e8318877a212e2af9a8502 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 10 Apr 2022 13:02:31 +0300 Subject: [PATCH 25/30] rand: fix needless array allocation in MT19937RNG.u64() --- vlib/rand/mt19937/mt19937.v | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vlib/rand/mt19937/mt19937.v b/vlib/rand/mt19937/mt19937.v index 6b0a173884..287d8e0de8 100644 --- a/vlib/rand/mt19937/mt19937.v +++ b/vlib/rand/mt19937/mt19937.v @@ -144,24 +144,25 @@ pub fn (mut rng MT19937RNG) u32() u32 { return u32(ans) } +const mag01 = [u64(0), u64(matrix_a)] + // u64 returns a pseudorandom 64bit int in range `[0, 2⁶⁴)`. [inline] pub fn (mut rng MT19937RNG) u64() u64 { - mag01 := [u64(0), u64(mt19937.matrix_a)] mut x := u64(0) mut i := int(0) if rng.mti >= mt19937.nn { for i = 0; i < mt19937.nn - mt19937.mm; i++ { x = (rng.state[i] & mt19937.um) | (rng.state[i + 1] & mt19937.lm) - rng.state[i] = rng.state[i + mt19937.mm] ^ (x >> 1) ^ mag01[int(x & 1)] + rng.state[i] = rng.state[i + mt19937.mm] ^ (x >> 1) ^ mt19937.mag01[int(x & 1)] } for i < mt19937.nn - 1 { x = (rng.state[i] & mt19937.um) | (rng.state[i + 1] & mt19937.lm) - rng.state[i] = rng.state[i + (mt19937.mm - mt19937.nn)] ^ (x >> 1) ^ mag01[int(x & 1)] + rng.state[i] = rng.state[i + (mt19937.mm - mt19937.nn)] ^ (x >> 1) ^ mt19937.mag01[int(x & 1)] i++ } x = (rng.state[mt19937.nn - 1] & mt19937.um) | (rng.state[0] & mt19937.lm) - rng.state[mt19937.nn - 1] = rng.state[mt19937.mm - 1] ^ (x >> 1) ^ mag01[int(x & 1)] + rng.state[mt19937.nn - 1] = rng.state[mt19937.mm - 1] ^ (x >> 1) ^ mt19937.mag01[int(x & 1)] rng.mti = 0 } x = rng.state[rng.mti] From a0e7a46be4d468ecf61b0e6cd7c81f11ddbd4233 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 10 Apr 2022 13:07:35 +0300 Subject: [PATCH 26/30] rand: speed up MT19937RNG.u64 --- vlib/rand/mt19937/mt19937.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vlib/rand/mt19937/mt19937.v b/vlib/rand/mt19937/mt19937.v index 287d8e0de8..3bdfcb1468 100644 --- a/vlib/rand/mt19937/mt19937.v +++ b/vlib/rand/mt19937/mt19937.v @@ -147,7 +147,7 @@ pub fn (mut rng MT19937RNG) u32() u32 { const mag01 = [u64(0), u64(matrix_a)] // u64 returns a pseudorandom 64bit int in range `[0, 2⁶⁴)`. -[inline] +[direct_array_access; inline] pub fn (mut rng MT19937RNG) u64() u64 { mut x := u64(0) mut i := int(0) From fa66183f432119c9d8fdac1e755d3eb1ea190205 Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 11 Apr 2022 15:07:23 +0800 Subject: [PATCH 27/30] checker: check error for map of generic struct init (#13999) --- vlib/v/checker/containers.v | 16 ++++++++++++++++ .../tests/map_of_generic_struct_init_err.out | 6 ++++++ .../tests/map_of_generic_struct_init_err.vv | 7 +++++++ vlib/v/parser/parser.v | 4 +++- 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/map_of_generic_struct_init_err.out create mode 100644 vlib/v/checker/tests/map_of_generic_struct_init_err.vv diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index bee64cdb80..754b1ef6cd 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -260,6 +260,22 @@ pub fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type { // `x := map[string]string` - set in parser if node.typ != 0 { info := c.table.sym(node.typ).map_info() + if info.value_type != 0 { + val_sym := c.table.sym(info.value_type) + if val_sym.kind == .struct_ { + val_info := val_sym.info as ast.Struct + if val_info.generic_types.len > 0 && val_info.concrete_types.len == 0 + && !info.value_type.has_flag(.generic) { + if c.table.cur_concrete_types.len == 0 { + c.error('generic struct `$val_sym.name` must specify type parameter, e.g. Foo', + node.pos) + } else { + c.error('generic struct `$val_sym.name` must specify type parameter, e.g. Foo', + node.pos) + } + } + } + } c.ensure_type_exists(info.key_type, node.pos) or {} c.ensure_type_exists(info.value_type, node.pos) or {} node.key_type = info.key_type diff --git a/vlib/v/checker/tests/map_of_generic_struct_init_err.out b/vlib/v/checker/tests/map_of_generic_struct_init_err.out new file mode 100644 index 0000000000..f9d68ef1c3 --- /dev/null +++ b/vlib/v/checker/tests/map_of_generic_struct_init_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/map_of_generic_struct_init_err.vv:6:11: error: generic struct `Item` must specify type parameter, e.g. Foo + 4 | + 5 | fn main() { + 6 | mut m := map[string]Item{} + | ~~~~~~~~~~~~~~~~~ + 7 | } diff --git a/vlib/v/checker/tests/map_of_generic_struct_init_err.vv b/vlib/v/checker/tests/map_of_generic_struct_init_err.vv new file mode 100644 index 0000000000..27d3e72596 --- /dev/null +++ b/vlib/v/checker/tests/map_of_generic_struct_init_err.vv @@ -0,0 +1,7 @@ +struct Item{ + val T +} + +fn main() { + mut m := map[string]Item{} +} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 91afa31779..61886980c0 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2140,10 +2140,12 @@ pub fn (mut p Parser) name_expr() ast.Expr { p.expr_mod = '' // `map[string]int` initialization if p.tok.lit == 'map' && p.peek_tok.kind == .lsbr { + mut pos := p.tok.pos() map_type := p.parse_map_type() if p.tok.kind == .lcbr { p.next() if p.tok.kind == .rcbr { + pos = pos.extend(p.tok.pos()) p.next() } else { if p.pref.is_fmt { @@ -2156,7 +2158,7 @@ pub fn (mut p Parser) name_expr() ast.Expr { } return ast.MapInit{ typ: map_type - pos: p.prev_tok.pos() + pos: pos } } // `chan typ{...}` From 1938bc48e7d8a7ec34477661ab3030556224c111 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 11 Apr 2022 08:12:04 +0100 Subject: [PATCH 28/30] toml.scanner: make end_of_text, at, next and peek return u32 (#13998) --- vlib/toml/scanner/scanner.v | 8 ++++---- vlib/toml/scanner/scanner_test.v | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/vlib/toml/scanner/scanner.v b/vlib/toml/scanner/scanner.v index 31bc4fdc3d..7b7d206f4b 100644 --- a/vlib/toml/scanner/scanner.v +++ b/vlib/toml/scanner/scanner.v @@ -10,7 +10,7 @@ import toml.util pub const ( digit_extras = [`_`, `.`, `x`, `o`, `b`, `e`, `E`] - end_of_text = -1 + end_of_text = math.max_u32 ) // Scanner contains the necessary fields for the state of the scan process. @@ -263,7 +263,7 @@ pub fn (s &Scanner) remaining() int { // next returns the next character code from the input text. // next returns `end_of_text` if it can't reach the next character. [direct_array_access; inline] -pub fn (mut s Scanner) next() int { +pub fn (mut s Scanner) next() u32 { if s.pos < s.text.len { opos := s.pos s.pos++ @@ -299,7 +299,7 @@ pub fn (mut s Scanner) skip_n(n int) { // at returns `end_of_text` if it can't get the current character. // unlike `next()`, `at()` does not change the state of the scanner. [direct_array_access; inline] -pub fn (s &Scanner) at() int { +pub fn (s &Scanner) at() u32 { if s.pos < s.text.len { return s.text[s.pos] } @@ -315,7 +315,7 @@ fn (s Scanner) at_crlf() bool { // peek returns the character code from the input text at position + `n`. // peek returns `end_of_text` if it can't peek `n` characters ahead. [direct_array_access; inline] -pub fn (s &Scanner) peek(n int) int { +pub fn (s &Scanner) peek(n int) u32 { if s.pos + n < s.text.len { // Allow peeking back - needed for spaces between date and time in RFC 3339 format :/ if n - 1 < 0 && s.pos + n - 1 >= 0 { diff --git a/vlib/toml/scanner/scanner_test.v b/vlib/toml/scanner/scanner_test.v index 1ec75e80eb..7ac35e8781 100644 --- a/vlib/toml/scanner/scanner_test.v +++ b/vlib/toml/scanner/scanner_test.v @@ -25,9 +25,9 @@ fn test_next() { assert s.next() == `a` assert s.next() == `b` assert s.next() == `c` - assert s.next() == -1 - assert s.next() == -1 - assert s.next() == -1 + assert s.next() == scanner.end_of_text + assert s.next() == scanner.end_of_text + assert s.next() == scanner.end_of_text } fn test_skip() { @@ -35,14 +35,14 @@ fn test_skip() { assert s.next() == `a` s.skip() assert s.next() == `c` - assert s.next() == -1 + assert s.next() == scanner.end_of_text } fn test_skip_n() { mut s := scanner.new_scanner(input: scan_input) or { panic(err) } s.skip_n(2) assert s.next() == `c` - assert s.next() == -1 + assert s.next() == scanner.end_of_text } fn test_at() { @@ -54,7 +54,7 @@ fn test_at() { assert s.next() == `a` assert s.next() == `b` assert s.next() == `c` - assert s.next() == -1 + assert s.next() == scanner.end_of_text } fn test_peek() { @@ -62,13 +62,13 @@ fn test_peek() { assert s.peek(0) == `a` assert s.peek(1) == `b` assert s.peek(2) == `c` - assert s.peek(3) == -1 - assert s.peek(4) == -1 + assert s.peek(3) == scanner.end_of_text + assert s.peek(4) == scanner.end_of_text // assert s.next() == `a` assert s.next() == `b` assert s.next() == `c` - assert s.next() == -1 + assert s.next() == scanner.end_of_text } fn test_reset() { @@ -76,7 +76,7 @@ fn test_reset() { assert s.next() == `a` s.next() s.next() - assert s.next() == -1 + assert s.next() == scanner.end_of_text s.reset() assert s.next() == `a` } From e4dfffd70baf952aa69c65ec6f57bf7ebd32abbc Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Mon, 11 Apr 2022 10:21:14 +0300 Subject: [PATCH 29/30] toml: remove math import (used only for math.max and math.max_u32) --- vlib/toml/scanner/scanner.v | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vlib/toml/scanner/scanner.v b/vlib/toml/scanner/scanner.v index 7b7d206f4b..7f421227c9 100644 --- a/vlib/toml/scanner/scanner.v +++ b/vlib/toml/scanner/scanner.v @@ -3,14 +3,13 @@ // that can be found in the LICENSE file. module scanner -import math import toml.input import toml.token import toml.util pub const ( digit_extras = [`_`, `.`, `x`, `o`, `b`, `e`, `E`] - end_of_text = math.max_u32 + end_of_text = 4294967295 ) // Scanner contains the necessary fields for the state of the scan process. @@ -346,7 +345,7 @@ fn (mut s Scanner) new_token(kind token.Kind, lit string, len int) token.Token { return token.Token{ kind: kind lit: lit - col: math.max(1, col) + col: if col < 1 { 1 } else { col } line_nr: s.line_nr + 1 pos: s.pos - s.header_len - len + 1 len: len From 843ce430779388f66da025d02f10656550cad47f Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Mon, 11 Apr 2022 12:01:47 +0300 Subject: [PATCH 30/30] strconv: cleanup atof.c.v - use a ParserState enum, clarify comments --- vlib/strconv/atof.c.v | 168 +++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 93 deletions(-) diff --git a/vlib/strconv/atof.c.v b/vlib/strconv/atof.c.v index e60f4a1e2f..11663976e3 100644 --- a/vlib/strconv/atof.c.v +++ b/vlib/strconv/atof.c.v @@ -1,26 +1,47 @@ module strconv -/* -atof util +// Copyright (c) 2019-2022 Dario Deledda. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// This file contains utilities for converting a string to a f64 variable. +// IEEE 754 standard is used. +// Know limitation: limited to 18 significant digits +// +// The code is inspired by: +// Grzegorz Kraszewski krashan@teleinfo.pb.edu.pl +// URL: http://krashan.ppa.pl/articles/stringtofloat/ +// Original license: MIT +// 96 bit operation utilities +// +// Note: when u128 will be available, these function can be refactored. -Copyright (c) 2019-2022 Dario Deledda. All rights reserved. -Use of this source code is governed by an MIT license -that can be found in the LICENSE file. +// f32 constants +pub const ( + single_plus_zero = u32(0x0000_0000) + single_minus_zero = u32(0x8000_0000) + single_plus_infinity = u32(0x7F80_0000) + single_minus_infinity = u32(0xFF80_0000) +) -This file contains utilities for convert a string in a f64 variable -IEEE 754 standard is used +// f64 constants +pub const ( + digits = 18 + double_plus_zero = u64(0x0000000000000000) + double_minus_zero = u64(0x8000000000000000) + double_plus_infinity = u64(0x7FF0000000000000) + double_minus_infinity = u64(0xFFF0000000000000) +) -Know limitation: -- limited to 18 significant digits - -The code is inspired by: -Grzegorz Kraszewski krashan@teleinfo.pb.edu.pl -URL: http://krashan.ppa.pl/articles/stringtofloat/ -Original license: MIT - -96 bit operation utilities -Note: when u128 will be available these function can be refactored -*/ +// char constants +pub const ( + c_dpoint = `.` + c_plus = `+` + c_minus = `-` + c_zero = `0` + c_nine = `9` + c_ten = u32(10) +) // right logical shift 96 bit fn lsr96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) { @@ -78,48 +99,7 @@ fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) { return r2, r1, r0 } -// Constants - -pub const ( - // - // f32 constants - // - single_plus_zero = u32(0x0000_0000) - single_minus_zero = u32(0x8000_0000) - single_plus_infinity = u32(0x7F80_0000) - single_minus_infinity = u32(0xFF80_0000) - // - // f64 constants - // - digits = 18 - double_plus_zero = u64(0x0000000000000000) - double_minus_zero = u64(0x8000000000000000) - double_plus_infinity = u64(0x7FF0000000000000) - double_minus_infinity = u64(0xFFF0000000000000) - // - // Possible parser return values. - // - parser_ok = 0 // parser finished OK - parser_pzero = 1 // no digits or number is smaller than +-2^-1022 - parser_mzero = 2 // number is negative, module smaller - parser_pinf = 3 // number is higher than +HUGE_VAL - parser_minf = 4 // number is lower than -HUGE_VAL - parser_invalid_number = 5 // invalid number, used for '#@%^' for example - // - // char constants - // Note: Modify these if working with non-ASCII encoding - // - c_dpoint = `.` - c_plus = `+` - c_minus = `-` - c_zero = `0` - c_nine = `9` - c_ten = u32(10) -) - // Utility functions - -// NOTE: Modify these if working with non-ASCII encoding fn is_digit(x byte) bool { return (x >= strconv.c_zero && x <= strconv.c_nine) == true } @@ -132,14 +112,21 @@ fn is_exp(x byte) bool { return (x == `E` || x == `e`) == true } -/* -String parser -NOTE: #TOFIX need one char after the last char of the number -*/ +// Possible parser return values. +enum ParserState { + ok // parser finished OK + pzero // no digits or number is smaller than +-2^-1022 + mzero // number is negative, module smaller + pinf // number is higher than +HUGE_VAL + minf // number is lower than -HUGE_VAL + invalid_number // invalid number, used for '#@%^' for example +} -fn parser(s string) (int, PrepNumber) { +// parser tries to parse the given string into a number +// NOTE: #TOFIX need one char after the last char of the number +fn parser(s string) (ParserState, PrepNumber) { mut digx := 0 - mut result := strconv.parser_ok + mut result := ParserState.ok mut expneg := false mut expexp := 0 mut i := 0 @@ -216,45 +203,45 @@ fn parser(s string) (int, PrepNumber) { pn.exponent += expexp if pn.mantissa == 0 { if pn.negative { - result = strconv.parser_mzero + result = .mzero } else { - result = strconv.parser_pzero + result = .pzero } } else if pn.exponent > 309 { if pn.negative { - result = strconv.parser_minf + result = .minf } else { - result = strconv.parser_pinf + result = .pinf } } else if pn.exponent < -328 { if pn.negative { - result = strconv.parser_mzero + result = .mzero } else { - result = strconv.parser_pzero + result = .pzero } } if i == 0 && s.len > 0 { - return strconv.parser_invalid_number, pn + return ParserState.invalid_number, pn } return result, pn } -/* -Converter to the bit form of the f64 number -*/ - -// converter return a u64 with the bit image of the f64 number +// converter returns a u64 with the bit image of the f64 number fn converter(mut pn PrepNumber) u64 { mut binexp := 92 - mut s2 := u32(0) // 96-bit precision integer + // s0,s1,s2 are the parts of a 96-bit precision integer + mut s2 := u32(0) mut s1 := u32(0) mut s0 := u32(0) - mut q2 := u32(0) // 96-bit precision integer + // q0,q1,q2 are the parts of a 96-bit precision integer + mut q2 := u32(0) mut q1 := u32(0) mut q0 := u32(0) - mut r2 := u32(0) // 96-bit precision integer + // r0,r1,r2 are the parts of a 96-bit precision integer + mut r2 := u32(0) mut r1 := u32(0) mut r0 := u32(0) + // mask28 := u32(u64(0xF) << 28) mut result := u64(0) // working on 3 u32 to have 96 bit precision @@ -404,35 +391,30 @@ fn converter(mut pn PrepNumber) u64 { return result } -// Public functions - -// atof64 return a f64 from a string doing a parsing operation +// atof64 parses the string `s`, and if possible, converts it into a f64 number pub fn atof64(s string) ?f64 { if s.len == 0 { return error('expected a number found an empty string') } - mut pn := PrepNumber{} - mut res_parsing := 0 mut res := Float64u{} - - res_parsing, pn = parser(s) + mut res_parsing, mut pn := parser(s) match res_parsing { - strconv.parser_ok { + .ok { res.u = converter(mut pn) } - strconv.parser_pzero { + .pzero { res.u = strconv.double_plus_zero } - strconv.parser_mzero { + .mzero { res.u = strconv.double_minus_zero } - strconv.parser_pinf { + .pinf { res.u = strconv.double_plus_infinity } - strconv.parser_minf { + .minf { res.u = strconv.double_minus_infinity } - else { + .invalid_number { return error('not a number') } }