diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index c6381c75c5..80e6cf41ce 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -123,6 +123,13 @@ pub fn (s string) cstr() byteptr { return clone.str } */ +pub fn (s string) replace_once(rep, with string) string { + index := s.index(rep) + if index != -1 { + return s.substr(0,index) + with + s.substr(index + rep.len, s.len) + } + return s +} pub fn (s string) replace(rep, with string) string { if s.len == 0 || rep.len == 0 { diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index af5b685e1a..091645f547 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -133,9 +133,19 @@ fn (p mut Parser) name_expr() string { // amp ptr := p.tok == .amp deref := p.tok == .mul - if ptr || deref { - p.next() - } + mut mul_nr := 0 + mut deref_nr := 0 + for { + if p.tok == .amp { + mul_nr++ + }else if p.tok == .mul { + deref_nr++ + }else { + break + } + p.next() + } + mut name := p.lit // Raw string (`s := r'hello \n ') if name == 'r' && p.peek() == .str { @@ -176,7 +186,7 @@ fn (p mut Parser) name_expr() string { // Variable, checked before modules, so that module shadowing is allowed: // `gg = gg.newcontext(); gg.draw_rect(...)` if p.known_var_check_new_var(name) { - return p.get_var_type(name, ptr, deref) + return p.get_var_type(name, ptr, deref_nr) } // Module? if p.peek() == .dot && (name == p.mod || @@ -204,7 +214,7 @@ fn (p mut Parser) name_expr() string { } // re-check if p.known_var_check_new_var(name) { - return p.get_var_type(name, ptr, deref) + return p.get_var_type(name, ptr, deref_nr) } // if known_type || is_c_struct_init || (p.first_pass() && p.peek() == .lcbr) { @@ -213,10 +223,10 @@ fn (p mut Parser) name_expr() string { // cast expression: float(5), byte(0), (*int)(ptr) etc if !is_c && ( p.peek() == .lpar || (deref && p.peek() == .rpar) ) { if deref { - name += '*' + name += '*'.repeat(deref_nr ) } else if ptr { - name += '*' + name += '*'.repeat(mul_nr) } p.gen('(') mut typ := name @@ -405,7 +415,7 @@ fn (p mut Parser) expression() string { if typ == 'bool' { p.error('operator ${p.tok.str()} not defined on bool ') } - is_num := typ == 'void*' || typ == 'byte*' || is_number_type(typ) + is_num := typ.contains('*') || is_number_type(typ) p.check_space(p.tok) if is_str && tok_op == .plus && !p.is_js { p.cgen.set_placeholder(ph, 'string_add(') @@ -421,7 +431,9 @@ fn (p mut Parser) expression() string { // Msvc errors on void* pointer arithmatic // ... So cast to byte* and then do the add p.cgen.set_placeholder(ph, '(byte*)') - } + }else if typ.contains('*') { + p.cgen.set_placeholder(ph, '($typ)') + } p.gen(tok_op.str()) } // Vec + Vec diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index bc78258b7b..9c15694ea1 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -1461,7 +1461,7 @@ fn (p mut Parser) get_struct_type(name_ string, is_c bool, is_ptr bool) string { return p.struct_init(name) } -fn (p mut Parser) get_var_type(name string, is_ptr bool, is_deref bool) string { +fn (p mut Parser) get_var_type(name string, is_ptr bool, deref_nr int) string { v := p.find_var_check_new_var(name) or { return "" } if name == '_' { p.error('cannot use `_` as value') @@ -1469,9 +1469,11 @@ fn (p mut Parser) get_var_type(name string, is_ptr bool, is_deref bool) string { if is_ptr { p.gen('&') } - else if is_deref { - p.gen('*') - } + else if deref_nr > 0 { + for _ in 0..deref_nr { + p.gen('*') + } + } if p.pref.autofree && v.typ == 'string' && v.is_arg && p.assigned_type == 'string' { p.warn('setting moved ' + v.typ) @@ -1479,7 +1481,7 @@ fn (p mut Parser) get_var_type(name string, is_ptr bool, is_deref bool) string { } mut typ := p.var_expr(v) // *var - if is_deref { + if deref_nr > 0 { /* if !p.inside_unsafe { p.error('dereferencing can only be done inside an `unsafe` block') @@ -1489,8 +1491,11 @@ fn (p mut Parser) get_var_type(name string, is_ptr bool, is_deref bool) string { println('name="$name", t=$v.typ') p.error('dereferencing requires a pointer, but got `$typ`') } - typ = typ.replace('ptr', '')// TODO - typ = typ.replace('*', '')// TODO + for _ in 0..deref_nr { + typ = typ.replace_once('ptr', '')// TODO + typ = typ.replace_once('*', '')// TODO + } + } // &var else if is_ptr { diff --git a/vlib/compiler/table.v b/vlib/compiler/table.v index 2e6867e898..e38fe3e74d 100644 --- a/vlib/compiler/table.v +++ b/vlib/compiler/table.v @@ -334,7 +334,7 @@ fn (table &Table) known_type(typ_ string) bool { mut typ := typ_ // 'byte*' => look up 'byte', but don't mess up fns if typ.ends_with('*') && !typ.contains(' ') { - typ = typ[..typ.len - 1] + typ = typ.replace('*', '') } t := table.typesmap[typ] return t.name.len > 0 && !t.is_placeholder @@ -558,7 +558,7 @@ fn (p &Parser) find_type(name string) Type { fn (t &Table) find_type(name_ string) Type { mut name := name_ if name.ends_with('*') && !name.contains(' ') { - name = name[..name.len - 1] + name = name.replace('*', '') } if !(name in t.typesmap) { //println('ret Type') diff --git a/vlib/compiler/tests/pointers_test.v b/vlib/compiler/tests/pointers_test.v new file mode 100644 index 0000000000..0c68096c80 --- /dev/null +++ b/vlib/compiler/tests/pointers_test.v @@ -0,0 +1,24 @@ + +fn test_pointer_arithmetic() { + arr := [1,2,3,4] + unsafe { + mut parr := *int(arr.data) + parr += 1 + assert 2 == *parr + } +} + +fn test_multi_level_pointer_dereferencing() { + n := 100 + pn := &n + ppn := &pn + + unsafe { + mut pppn := &ppn + ***pppn = 300 + pppa := ***int(pppn) + assert 300 == ***pppa + } + + assert n == 300 // updated by the unsafe pointer manipulation +}