v.scanner: fix ambiguity of two-level generics and shift-right (#11540)
parent
76f70d51f3
commit
b343f19bec
|
@ -1274,6 +1274,13 @@ pub fn (c byte) is_letter() bool {
|
||||||
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
|
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// is_alnum returns `true` if the byte is in range a-z, A-Z, 0-9 and `false` otherwise.
|
||||||
|
// Example: assert byte(`V`) == true
|
||||||
|
[inline]
|
||||||
|
pub fn (c byte) is_alnum() bool {
|
||||||
|
return c.is_letter() || c.is_digit()
|
||||||
|
}
|
||||||
|
|
||||||
// free allows for manually freeing the memory occupied by the string
|
// free allows for manually freeing the memory occupied by the string
|
||||||
[manualfree; unsafe]
|
[manualfree; unsafe]
|
||||||
pub fn (s &string) free() {
|
pub fn (s &string) free() {
|
||||||
|
|
|
@ -1534,7 +1534,7 @@ pub fn (mut f Fmt) call_expr(node ast.CallExpr) {
|
||||||
f.comments(arg.comments)
|
f.comments(arg.comments)
|
||||||
}
|
}
|
||||||
if node.is_method {
|
if node.is_method {
|
||||||
if node.name in ['map', 'filter'] {
|
if node.name in ['map', 'filter', 'all', 'any'] {
|
||||||
f.inside_lambda = true
|
f.inside_lambda = true
|
||||||
defer {
|
defer {
|
||||||
f.inside_lambda = false
|
f.inside_lambda = false
|
||||||
|
|
|
@ -11,6 +11,7 @@ import v.pref
|
||||||
import v.util
|
import v.util
|
||||||
import v.vet
|
import v.vet
|
||||||
import v.errors
|
import v.errors
|
||||||
|
import v.ast
|
||||||
|
|
||||||
const (
|
const (
|
||||||
single_quote = `'`
|
single_quote = `'`
|
||||||
|
@ -36,6 +37,7 @@ pub mut:
|
||||||
is_inter_end bool
|
is_inter_end bool
|
||||||
is_enclosed_inter bool
|
is_enclosed_inter bool
|
||||||
line_comment string
|
line_comment string
|
||||||
|
last_lt int = -1 // position of latest <
|
||||||
// prev_tok TokenKind
|
// prev_tok TokenKind
|
||||||
is_started bool
|
is_started bool
|
||||||
is_print_line_on_error bool
|
is_print_line_on_error bool
|
||||||
|
@ -917,12 +919,49 @@ fn (mut s Scanner) text_scan() token.Token {
|
||||||
return s.new_token(.ge, '', 2)
|
return s.new_token(.ge, '', 2)
|
||||||
} else if nextc == `>` {
|
} else if nextc == `>` {
|
||||||
if s.pos + 2 < s.text.len {
|
if s.pos + 2 < s.text.len {
|
||||||
if s.text[s.pos + 2] == `=` {
|
// first eat the possible spaces eg `>> (` => `>>(`
|
||||||
s.pos += 2
|
mut non_space_pos := s.pos + 2
|
||||||
return s.new_token(.right_shift_assign, '', 3)
|
for non_space_pos < s.text.len && s.text[non_space_pos].is_space() {
|
||||||
} else if s.text[s.pos + 2] in [`(`, `)`, `{`, `>`, `,`] {
|
non_space_pos++
|
||||||
// multi-level generics such as Foo<Bar<baz>>{ }, func<Bar<baz>>( ), etc
|
}
|
||||||
return s.new_token(.gt, '', 1)
|
match s.text[non_space_pos] {
|
||||||
|
`=` {
|
||||||
|
s.pos += 2
|
||||||
|
return s.new_token(.right_shift_assign, '', 3)
|
||||||
|
}
|
||||||
|
// definite generic cases such as Foo<Bar<int>>{}
|
||||||
|
`)`, `{`, `}`, `,`, `>`, `[`, `]` {
|
||||||
|
return s.new_token(.gt, '', 1)
|
||||||
|
}
|
||||||
|
// notice two-level generic call and shift-right share the rest patterns
|
||||||
|
// such as `foo<Baz, Bar<int>>(a)` vs `a, b := Foo{}<Foo{}, bar>>(baz)`
|
||||||
|
// which is hard but could be discriminated by my following algorithm
|
||||||
|
// @SleepyRoy if you have smarter algorithm :-)
|
||||||
|
else {
|
||||||
|
// almost correct heuristics: 2-level generic call's last <T> cannot be extremely long
|
||||||
|
// here we set the limit 100 which should be nice for real cases
|
||||||
|
if s.last_lt >= 0 && s.pos - s.last_lt < 100 {
|
||||||
|
// ...Bar<int, []Foo, [20]f64, map[string][]bool>> =>
|
||||||
|
// int, []Foo, [20]f64, map[string][]bool =>
|
||||||
|
// int, Foo, f64, bool
|
||||||
|
typs := s.text[s.last_lt + 1..s.pos].trim_right('>').split(',').map(it.trim_space().trim_right('>').after(']'))
|
||||||
|
// if any typ is neither builtin nor Type, then the case is not generics
|
||||||
|
for typ in typs {
|
||||||
|
if typ.len == 0 {
|
||||||
|
s.pos++
|
||||||
|
return s.new_token(.right_shift, '', 2)
|
||||||
|
}
|
||||||
|
if typ !in ast.builtin_type_names && !(typ[0].is_capital()
|
||||||
|
&& typ[1..].bytes().all(it.is_alnum())) {
|
||||||
|
s.pos++
|
||||||
|
return s.new_token(.right_shift, '', 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.new_token(.gt, '', 1)
|
||||||
|
}
|
||||||
|
s.pos++
|
||||||
|
return s.new_token(.right_shift, '', 2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.pos++
|
s.pos++
|
||||||
|
@ -946,6 +985,7 @@ fn (mut s Scanner) text_scan() token.Token {
|
||||||
s.pos++
|
s.pos++
|
||||||
return s.new_token(.arrow, '', 2)
|
return s.new_token(.arrow, '', 2)
|
||||||
} else {
|
} else {
|
||||||
|
s.last_lt = s.pos
|
||||||
return s.new_token(.lt, '', 1)
|
return s.new_token(.lt, '', 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -510,6 +510,21 @@ fn test_multi_level_generics() {
|
||||||
two) == 20
|
two) == 20
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Empty {}
|
||||||
|
|
||||||
|
fn (e1 Empty) < (e2 Empty) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TandU<T, U> {
|
||||||
|
t T
|
||||||
|
u U
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boring_function<T>(t T) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
fn test_generic_detection() {
|
fn test_generic_detection() {
|
||||||
v1, v2 := -1, 1
|
v1, v2 := -1, 1
|
||||||
|
|
||||||
|
@ -530,4 +545,43 @@ fn test_generic_detection() {
|
||||||
assert multi_generic_args<[]int, int>([]int{}, 0)
|
assert multi_generic_args<[]int, int>([]int{}, 0)
|
||||||
assert multi_generic_args<map[int]int, int>(map[int]int{}, 0)
|
assert multi_generic_args<map[int]int, int>(map[int]int{}, 0)
|
||||||
assert 0 < return_one<int>(10, 0)
|
assert 0 < return_one<int>(10, 0)
|
||||||
|
|
||||||
|
// "the hardest cases"
|
||||||
|
foo, bar, baz := 1, 2, 16
|
||||||
|
res1, res2 := foo < bar, baz >> (foo + 1 - 1)
|
||||||
|
assert res1
|
||||||
|
assert res2 == 8
|
||||||
|
res3, res4 := Empty{} < Empty{}, baz >> (foo + 1 - 1)
|
||||||
|
assert res3
|
||||||
|
assert res4 == 8
|
||||||
|
assert boring_function<TandU<Empty, int>>(TandU<Empty, int>{
|
||||||
|
t: Empty{}
|
||||||
|
u: 10
|
||||||
|
})
|
||||||
|
|
||||||
|
assert boring_function<MultiLevel<MultiLevel<int>>>(MultiLevel<MultiLevel<int>>{
|
||||||
|
foo: MultiLevel<int>{
|
||||||
|
foo: 10
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert boring_function<TandU<MultiLevel<int>, []int>>(TandU<MultiLevel<int>, []int>{
|
||||||
|
t: MultiLevel<int>{
|
||||||
|
foo: 10
|
||||||
|
}
|
||||||
|
u: [10]
|
||||||
|
})
|
||||||
|
|
||||||
|
// this final case challenges your scanner :-)
|
||||||
|
assert boring_function<TandU<TandU<int,MultiLevel<Empty>>, map[string][]int>>(TandU<TandU<int,MultiLevel<Empty>>, map[string][]int>{
|
||||||
|
t: TandU<int,MultiLevel<Empty>>{
|
||||||
|
t: 20
|
||||||
|
u: MultiLevel<Empty>{
|
||||||
|
foo: Empty{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u: {
|
||||||
|
'bar': [40]
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue