all: support slices with negative indexes `#[start..end]` (gated arrays) (#12914)
							parent
							
								
									2b9f993574
								
							
						
					
					
						commit
						278c08704c
					
				|  | @ -384,6 +384,57 @@ fn (a array) slice(start int, _end int) array { | ||||||
| 	return res | 	return res | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // slice_ni returns an array using the same buffer as original array
 | ||||||
|  | // but starting from the `start` element and ending with the element before
 | ||||||
|  | // the `end` element of the original array.
 | ||||||
|  | // This function can use negative indexes `a.slice_ni(-3, a.len)`
 | ||||||
|  | // that get the last 3 elements of the array otherwise it return an empty array.
 | ||||||
|  | // This function always return a valid array.
 | ||||||
|  | fn (a array) slice_ni(_start int, _end int) array { | ||||||
|  | 	mut end := _end | ||||||
|  | 	mut start := _start | ||||||
|  | 
 | ||||||
|  | 	if start < 0 { | ||||||
|  | 		start = a.len + start | ||||||
|  | 		if start < 0 { | ||||||
|  | 			start = 0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if end < 0 { | ||||||
|  | 		end = a.len + end | ||||||
|  | 		if end < 0 { | ||||||
|  | 			end = 0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if end >= a.len { | ||||||
|  | 		end = a.len | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if start >= a.len || start > end { | ||||||
|  | 		res := array{ | ||||||
|  | 			element_size: a.element_size | ||||||
|  | 			data: a.data | ||||||
|  | 			offset: 0 | ||||||
|  | 			len: 0 | ||||||
|  | 			cap: 0 | ||||||
|  | 		} | ||||||
|  | 		return res | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	offset := start * a.element_size | ||||||
|  | 	data := unsafe { &byte(a.data) + offset } | ||||||
|  | 	l := end - start | ||||||
|  | 	res := array{ | ||||||
|  | 		element_size: a.element_size | ||||||
|  | 		data: data | ||||||
|  | 		offset: a.offset + offset | ||||||
|  | 		len: l | ||||||
|  | 		cap: l | ||||||
|  | 	} | ||||||
|  | 	return res | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // used internally for [2..4]
 | // used internally for [2..4]
 | ||||||
| fn (a array) slice2(start int, _end int, end_max bool) array { | fn (a array) slice2(start int, _end int, end_max bool) array { | ||||||
| 	end := if end_max { a.len } else { _end } | 	end := if end_max { a.len } else { _end } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | fn test_gated_arrays() { | ||||||
|  | 	a := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | ||||||
|  | 	assert a#[-1..] == [9] | ||||||
|  | 	assert a#[..-9] == [0] | ||||||
|  | 	assert a#[-9..-7] == [1, 2] | ||||||
|  | 	assert a#[-2..] == [8, 9] | ||||||
|  | 
 | ||||||
|  | 	// fixed array
 | ||||||
|  | 	a1 := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]! | ||||||
|  | 	assert a1#[-1..] == [9] | ||||||
|  | 	assert a1#[..-9] == [0] | ||||||
|  | 	assert a1#[-9..-7] == [1, 2] | ||||||
|  | 	assert a1#[-2..] == [8, 9] | ||||||
|  | 
 | ||||||
|  | 	// empty array
 | ||||||
|  | 	assert a#[-3..-4] == [] // start > end
 | ||||||
|  | 	assert a#[20..] == [] // start > array.len
 | ||||||
|  | 	assert a#[-20..-10] == [] // start+len < 0
 | ||||||
|  | 	assert a#[20..-9] == [] // start > end  && start > end
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn test_gated_strings() { | ||||||
|  | 	a := '0123456789' | ||||||
|  | 	assert a#[-1..] == '9' | ||||||
|  | 	assert a#[..-9] == '0' | ||||||
|  | 	assert a#[-9..-7] == '12' | ||||||
|  | 	assert a#[-2..] == '89' | ||||||
|  | 
 | ||||||
|  | 	// empty string
 | ||||||
|  | 	assert a#[-3..-4] == '' // start > end
 | ||||||
|  | 	assert a#[20..] == '' // start > array.len
 | ||||||
|  | 	assert a#[-20..-10] == '' // start+len < 0
 | ||||||
|  | 	assert a#[20..-9] == '' // start > end  && start > end
 | ||||||
|  | 
 | ||||||
|  | 	//
 | ||||||
|  | 	// test negative indexes in slices from github discussion
 | ||||||
|  | 	//
 | ||||||
|  | 	s := '0123456789' | ||||||
|  | 
 | ||||||
|  | 	// normal behaviour
 | ||||||
|  | 	assert s#[1..3] == '12' | ||||||
|  | 	assert s#[..3] == '012' | ||||||
|  | 	assert s#[8..] == '89' | ||||||
|  | 
 | ||||||
|  | 	// negative indexes behaviour
 | ||||||
|  | 	assert s#[-2..] == '89' | ||||||
|  | 	assert s#[..-8] == '01' | ||||||
|  | 	assert s#[2..-2] == '234567' | ||||||
|  | 	assert s#[-12..-16] == '' | ||||||
|  | 	assert s#[-8..-2] == '234567' | ||||||
|  | 
 | ||||||
|  | 	// out of bound both indexes
 | ||||||
|  | 	assert s#[12..14] == '' | ||||||
|  | 	assert s#[-12..16] == '0123456789' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn test_gated_mixed_strings() { | ||||||
|  | 	//
 | ||||||
|  | 	// test negative indexes in slices
 | ||||||
|  | 	//
 | ||||||
|  | 	a := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | ||||||
|  | 
 | ||||||
|  | 	// normal behaviour
 | ||||||
|  | 	assert a#[1..3].str() == '[1, 2]' | ||||||
|  | 	assert a#[..3].str() == '[0, 1, 2]' | ||||||
|  | 	assert a#[8..].str() == '[8, 9]' | ||||||
|  | 
 | ||||||
|  | 	// negative indexes behaviour
 | ||||||
|  | 	assert a#[-2..].str() == '[8, 9]' | ||||||
|  | 	assert a#[..-8].str() == '[0, 1]' | ||||||
|  | 	assert a#[2..-2].str() == '[2, 3, 4, 5, 6, 7]' | ||||||
|  | 	assert a#[-12..-16].str() == '[]' | ||||||
|  | 	assert a#[-8..-2].str() == '[2, 3, 4, 5, 6, 7]' | ||||||
|  | 
 | ||||||
|  | 	// out of bound both indexes
 | ||||||
|  | 	assert a#[12..14].str() == '[]' | ||||||
|  | 	assert a#[-12..16].str() == a.str() | ||||||
|  | } | ||||||
|  | @ -788,6 +788,60 @@ pub fn (s string) substr(start int, end int) string { | ||||||
| 	return res | 	return res | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // substr_ni returns the string between index positions `start` and `end` allowing negative indexes
 | ||||||
|  | // This function always return a valid string.
 | ||||||
|  | [direct_array_access] | ||||||
|  | pub fn (s string) substr_ni(_start int, _end int) string { | ||||||
|  | 	mut start := _start | ||||||
|  | 	mut end := _end | ||||||
|  | 
 | ||||||
|  | 	// borders math
 | ||||||
|  | 	if start < 0 { | ||||||
|  | 		start = s.len + start | ||||||
|  | 		if start < 0 { | ||||||
|  | 			start = 0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if end < 0 { | ||||||
|  | 		end = s.len + end | ||||||
|  | 		if end < 0 { | ||||||
|  | 			end = 0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if end >= s.len { | ||||||
|  | 		end = s.len | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if start > s.len || end < start { | ||||||
|  | 		mut res := string{ | ||||||
|  | 			str: unsafe { malloc_noscan(1) } | ||||||
|  | 			len: 0 | ||||||
|  | 		} | ||||||
|  | 		unsafe { | ||||||
|  | 			res.str[0] = 0 | ||||||
|  | 		} | ||||||
|  | 		return res | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	len := end - start | ||||||
|  | 
 | ||||||
|  | 	// string copy
 | ||||||
|  | 	mut res := string{ | ||||||
|  | 		str: unsafe { malloc_noscan(len + 1) } | ||||||
|  | 		len: len | ||||||
|  | 	} | ||||||
|  | 	for i in 0 .. len { | ||||||
|  | 		unsafe { | ||||||
|  | 			res.str[i] = s.str[start + i] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	unsafe { | ||||||
|  | 		res.str[len] = 0 | ||||||
|  | 	} | ||||||
|  | 	return res | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // index returns the position of the first character of the input string.
 | // index returns the position of the first character of the input string.
 | ||||||
| // It will return `-1` if the input string can't be found.
 | // It will return `-1` if the input string can't be found.
 | ||||||
| [direct_array_access] | [direct_array_access] | ||||||
|  |  | ||||||
|  | @ -822,6 +822,7 @@ pub mut: | ||||||
| 	is_farray bool | 	is_farray bool | ||||||
| 	is_option bool // IfGuard
 | 	is_option bool // IfGuard
 | ||||||
| 	is_direct bool // Set if the underlying memory can be safely accessed
 | 	is_direct bool // Set if the underlying memory can be safely accessed
 | ||||||
|  | 	is_gated  bool // #[] gated array
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct IfExpr { | pub struct IfExpr { | ||||||
|  | @ -1207,6 +1208,7 @@ pub: | ||||||
| 	has_high bool | 	has_high bool | ||||||
| 	has_low  bool | 	has_low  bool | ||||||
| 	pos      token.Position | 	pos      token.Position | ||||||
|  | 	is_gated bool // #[] gated array
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct CastExpr { | pub struct CastExpr { | ||||||
|  |  | ||||||
|  | @ -4331,7 +4331,7 @@ pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type { | ||||||
| 	return right_type | 	return right_type | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_type ast.Type, pos token.Position, range_index bool) { | fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_type ast.Type, pos token.Position, range_index bool, is_gated bool) { | ||||||
| 	index_type_sym := c.table.sym(index_type) | 	index_type_sym := c.table.sym(index_type) | ||||||
| 	// println('index expr left=$typ_sym.name $node.pos.line_nr')
 | 	// println('index expr left=$typ_sym.name $node.pos.line_nr')
 | ||||||
| 	// if typ_sym.kind == .array && (!(ast.type_idx(index_type) in ast.number_type_idxs) &&
 | 	// if typ_sym.kind == .array && (!(ast.type_idx(index_type) in ast.number_type_idxs) &&
 | ||||||
|  | @ -4345,7 +4345,7 @@ fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_ty | ||||||
| 			} | 			} | ||||||
| 			c.error('$type_str', pos) | 			c.error('$type_str', pos) | ||||||
| 		} | 		} | ||||||
| 		if index is ast.IntegerLiteral { | 		if index is ast.IntegerLiteral && !is_gated { | ||||||
| 			if index.val[0] == `-` { | 			if index.val[0] == `-` { | ||||||
| 				c.error('negative index `$index.val`', index.pos) | 				c.error('negative index `$index.val`', index.pos) | ||||||
| 			} else if typ_sym.kind == .array_fixed { | 			} else if typ_sym.kind == .array_fixed { | ||||||
|  | @ -4428,11 +4428,11 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { | ||||||
| 	if mut node.index is ast.RangeExpr { // [1..2]
 | 	if mut node.index is ast.RangeExpr { // [1..2]
 | ||||||
| 		if node.index.has_low { | 		if node.index.has_low { | ||||||
| 			index_type := c.expr(node.index.low) | 			index_type := c.expr(node.index.low) | ||||||
| 			c.check_index(typ_sym, node.index.low, index_type, node.pos, true) | 			c.check_index(typ_sym, node.index.low, index_type, node.pos, true, node.is_gated) | ||||||
| 		} | 		} | ||||||
| 		if node.index.has_high { | 		if node.index.has_high { | ||||||
| 			index_type := c.expr(node.index.high) | 			index_type := c.expr(node.index.high) | ||||||
| 			c.check_index(typ_sym, node.index.high, index_type, node.pos, true) | 			c.check_index(typ_sym, node.index.high, index_type, node.pos, true, node.is_gated) | ||||||
| 		} | 		} | ||||||
| 		// array[1..2] => array
 | 		// array[1..2] => array
 | ||||||
| 		// fixed_array[1..2] => array
 | 		// fixed_array[1..2] => array
 | ||||||
|  | @ -4460,7 +4460,11 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			index_type := c.expr(node.index) | 			index_type := c.expr(node.index) | ||||||
| 			c.check_index(typ_sym, node.index, index_type, node.pos, false) | 			// for [1] case #[1] is not allowed!
 | ||||||
|  | 			if node.is_gated == true { | ||||||
|  | 				c.error('`#[]` allowed only for ranges', node.pos) | ||||||
|  | 			} | ||||||
|  | 			c.check_index(typ_sym, node.index, index_type, node.pos, false, false) | ||||||
| 		} | 		} | ||||||
| 		value_type := c.table.value_type(typ) | 		value_type := c.table.value_type(typ) | ||||||
| 		if value_type != ast.void_type { | 		if value_type != ast.void_type { | ||||||
|  |  | ||||||
|  | @ -1898,6 +1898,11 @@ pub fn (mut f Fmt) if_guard_expr(node ast.IfGuardExpr) { | ||||||
| 
 | 
 | ||||||
| pub fn (mut f Fmt) index_expr(node ast.IndexExpr) { | pub fn (mut f Fmt) index_expr(node ast.IndexExpr) { | ||||||
| 	f.expr(node.left) | 	f.expr(node.left) | ||||||
|  | 	if node.index is ast.RangeExpr { | ||||||
|  | 		if node.index.is_gated { | ||||||
|  | 			f.write('#') | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	f.write('[') | 	f.write('[') | ||||||
| 	f.expr(node.index) | 	f.expr(node.index) | ||||||
| 	f.write(']') | 	f.write(']') | ||||||
|  |  | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | a := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | ||||||
|  | assert a#[-1..] == [9] | ||||||
|  | assert a#[..-9] == [0] | ||||||
|  | assert a#[-9..-7] == [1, 2] | ||||||
|  | assert a#[-2..] == [8, 9] | ||||||
|  | @ -60,10 +60,18 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) { | ||||||
| fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) { | fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) { | ||||||
| 	sym := g.table.final_sym(node.left_type) | 	sym := g.table.final_sym(node.left_type) | ||||||
| 	if sym.kind == .string { | 	if sym.kind == .string { | ||||||
|  | 		if node.is_gated { | ||||||
|  | 			g.write('string_substr_ni(') | ||||||
|  | 		} else { | ||||||
| 			g.write('string_substr(') | 			g.write('string_substr(') | ||||||
|  | 		} | ||||||
| 		g.expr(node.left) | 		g.expr(node.left) | ||||||
| 	} else if sym.kind == .array { | 	} else if sym.kind == .array { | ||||||
|  | 		if node.is_gated { | ||||||
|  | 			g.write('array_slice_ni(') | ||||||
|  | 		} else { | ||||||
| 			g.write('array_slice(') | 			g.write('array_slice(') | ||||||
|  | 		} | ||||||
| 		if node.left_type.is_ptr() { | 		if node.left_type.is_ptr() { | ||||||
| 			g.write('*') | 			g.write('*') | ||||||
| 		} | 		} | ||||||
|  | @ -72,7 +80,12 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) { | ||||||
| 		// Convert a fixed array to V array when doing `fixed_arr[start..end]`
 | 		// Convert a fixed array to V array when doing `fixed_arr[start..end]`
 | ||||||
| 		info := sym.info as ast.ArrayFixed | 		info := sym.info as ast.ArrayFixed | ||||||
| 		noscan := g.check_noscan(info.elem_type) | 		noscan := g.check_noscan(info.elem_type) | ||||||
| 		g.write('array_slice(new_array_from_c_array${noscan}(') | 		if node.is_gated { | ||||||
|  | 			g.write('array_slice_ni(') | ||||||
|  | 		} else { | ||||||
|  | 			g.write('array_slice(') | ||||||
|  | 		} | ||||||
|  | 		g.write('new_array_from_c_array${noscan}(') | ||||||
| 		g.write('$info.size') | 		g.write('$info.size') | ||||||
| 		g.write(', $info.size') | 		g.write(', $info.size') | ||||||
| 		g.write(', sizeof(') | 		g.write(', sizeof(') | ||||||
|  |  | ||||||
|  | @ -361,8 +361,15 @@ pub fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_iden | ||||||
| 				return node | 				return node | ||||||
| 			} | 			} | ||||||
| 			p.is_stmt_ident = is_stmt_ident | 			p.is_stmt_ident = is_stmt_ident | ||||||
| 		} else if p.tok.kind == .lsbr && (p.inside_fn || p.tok.line_nr == p.prev_tok.line_nr) { | 		} else if p.tok.kind in [.lsbr, .nilsbr] | ||||||
| 			node = p.index_expr(node) | 			&& (p.inside_fn || p.tok.line_nr == p.prev_tok.line_nr) { | ||||||
|  | 			// node = p.index_expr(node)
 | ||||||
|  | 			if p.tok.kind == .nilsbr { | ||||||
|  | 				node = p.index_expr(node, true) | ||||||
|  | 			} else { | ||||||
|  | 				node = p.index_expr(node, false) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			p.is_stmt_ident = is_stmt_ident | 			p.is_stmt_ident = is_stmt_ident | ||||||
| 			if p.tok.kind == .lpar && p.tok.line_nr == p.prev_tok.line_nr && node is ast.IndexExpr { | 			if p.tok.kind == .lpar && p.tok.line_nr == p.prev_tok.line_nr && node is ast.IndexExpr { | ||||||
| 				p.next() | 				p.next() | ||||||
|  |  | ||||||
|  | @ -8,8 +8,8 @@ import v.ast | ||||||
| import v.util | import v.util | ||||||
| import v.token | import v.token | ||||||
| 
 | 
 | ||||||
| pub fn (mut p Parser) parse_array_type() ast.Type { | pub fn (mut p Parser) parse_array_type(expecting token.Kind) ast.Type { | ||||||
| 	p.check(.lsbr) | 	p.check(expecting) | ||||||
| 	// fixed array
 | 	// fixed array
 | ||||||
| 	if p.tok.kind in [.number, .name] { | 	if p.tok.kind in [.number, .name] { | ||||||
| 		mut fixed_size := 0 | 		mut fixed_size := 0 | ||||||
|  | @ -88,7 +88,7 @@ pub fn (mut p Parser) parse_array_type() ast.Type { | ||||||
| 	mut nr_dims := 1 | 	mut nr_dims := 1 | ||||||
| 	// detect attr
 | 	// detect attr
 | ||||||
| 	not_attr := p.peek_tok.kind != .name && p.peek_token(2).kind !in [.semicolon, .rsbr] | 	not_attr := p.peek_tok.kind != .name && p.peek_token(2).kind !in [.semicolon, .rsbr] | ||||||
| 	for p.tok.kind == .lsbr && not_attr { | 	for p.tok.kind == expecting && not_attr { | ||||||
| 		p.next() | 		p.next() | ||||||
| 		p.check(.rsbr) | 		p.check(.rsbr) | ||||||
| 		nr_dims++ | 		nr_dims++ | ||||||
|  | @ -408,9 +408,9 @@ pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_d | ||||||
| 			// func
 | 			// func
 | ||||||
| 			return p.parse_fn_type('') | 			return p.parse_fn_type('') | ||||||
| 		} | 		} | ||||||
| 		.lsbr { | 		.lsbr, .nilsbr { | ||||||
| 			// array
 | 			// array
 | ||||||
| 			return p.parse_array_type() | 			return p.parse_array_type(p.tok.kind) | ||||||
| 		} | 		} | ||||||
| 		.lpar { | 		.lpar { | ||||||
| 			// multiple return
 | 			// multiple return
 | ||||||
|  |  | ||||||
|  | @ -2354,7 +2354,7 @@ pub fn (mut p Parser) name_expr() ast.Expr { | ||||||
| 	return node | 	return node | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr { | fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr { | ||||||
| 	// left == `a` in `a[0]`
 | 	// left == `a` in `a[0]`
 | ||||||
| 	start_pos := p.tok.position() | 	start_pos := p.tok.position() | ||||||
| 	p.next() // [
 | 	p.next() // [
 | ||||||
|  | @ -2379,7 +2379,9 @@ fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr { | ||||||
| 				high: high | 				high: high | ||||||
| 				has_high: has_high | 				has_high: has_high | ||||||
| 				pos: pos | 				pos: pos | ||||||
|  | 				is_gated: is_gated | ||||||
| 			} | 			} | ||||||
|  | 			is_gated: is_gated | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	expr := p.expr(0) // `[expr]` or  `[expr..`
 | 	expr := p.expr(0) // `[expr]` or  `[expr..`
 | ||||||
|  | @ -2403,7 +2405,9 @@ fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr { | ||||||
| 				has_high: has_high | 				has_high: has_high | ||||||
| 				has_low: has_low | 				has_low: has_low | ||||||
| 				pos: pos | 				pos: pos | ||||||
|  | 				is_gated: is_gated | ||||||
| 			} | 			} | ||||||
|  | 			is_gated: is_gated | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	// [expr]
 | 	// [expr]
 | ||||||
|  | @ -2433,6 +2437,7 @@ fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr { | ||||||
| 					stmts: or_stmts | 					stmts: or_stmts | ||||||
| 					pos: or_pos | 					pos: or_pos | ||||||
| 				} | 				} | ||||||
|  | 				is_gated: is_gated | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// `a[i] ?`
 | 		// `a[i] ?`
 | ||||||
|  | @ -2451,6 +2456,7 @@ fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr { | ||||||
| 			stmts: or_stmts | 			stmts: or_stmts | ||||||
| 			pos: or_pos | 			pos: or_pos | ||||||
| 		} | 		} | ||||||
|  | 		is_gated: is_gated | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -913,6 +913,12 @@ fn (mut s Scanner) text_scan() token.Token { | ||||||
| 				return s.new_token(.dot, '', 1) | 				return s.new_token(.dot, '', 1) | ||||||
| 			} | 			} | ||||||
| 			`#` { | 			`#` { | ||||||
|  | 				// manage gated arrays/strings
 | ||||||
|  | 				if nextc == `[` { | ||||||
|  | 					s.pos++ | ||||||
|  | 					return s.new_token(.nilsbr, '', 2) | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				start := s.pos + 1 | 				start := s.pos + 1 | ||||||
| 				s.ignore_line() | 				s.ignore_line() | ||||||
| 				if nextc == `!` { | 				if nextc == `!` { | ||||||
|  |  | ||||||
|  | @ -69,6 +69,7 @@ pub enum Kind { | ||||||
| 	lpar // (
 | 	lpar // (
 | ||||||
| 	rpar // )
 | 	rpar // )
 | ||||||
| 	lsbr // [
 | 	lsbr // [
 | ||||||
|  | 	nilsbr // #[
 | ||||||
| 	rsbr // ]
 | 	rsbr // ]
 | ||||||
| 	eq // ==
 | 	eq // ==
 | ||||||
| 	ne // !=
 | 	ne // !=
 | ||||||
|  | @ -243,6 +244,7 @@ fn build_token_str() []string { | ||||||
| 	s[Kind.lpar] = '(' | 	s[Kind.lpar] = '(' | ||||||
| 	s[Kind.rpar] = ')' | 	s[Kind.rpar] = ')' | ||||||
| 	s[Kind.lsbr] = '[' | 	s[Kind.lsbr] = '[' | ||||||
|  | 	s[Kind.nilsbr] = '#[' | ||||||
| 	s[Kind.rsbr] = ']' | 	s[Kind.rsbr] = ']' | ||||||
| 	s[Kind.eq] = '==' | 	s[Kind.eq] = '==' | ||||||
| 	s[Kind.ne] = '!=' | 	s[Kind.ne] = '!=' | ||||||
|  | @ -379,6 +381,7 @@ pub enum Precedence { | ||||||
| pub fn build_precedences() []Precedence { | pub fn build_precedences() []Precedence { | ||||||
| 	mut p := []Precedence{len: int(Kind._end_)} | 	mut p := []Precedence{len: int(Kind._end_)} | ||||||
| 	p[Kind.lsbr] = .index | 	p[Kind.lsbr] = .index | ||||||
|  | 	p[Kind.nilsbr] = .index | ||||||
| 	p[Kind.dot] = .call | 	p[Kind.dot] = .call | ||||||
| 	// `++` | `--` | `?`
 | 	// `++` | `--` | `?`
 | ||||||
| 	p[Kind.inc] = .postfix | 	p[Kind.inc] = .postfix | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue