Skip to content

Commit 537fa1b

Browse files
authored
comptime, ast: support [$d('s', 4)]int{}, move resolving to method on ComptimeCall (#21701)
1 parent 5b7cc63 commit 537fa1b

12 files changed

+110
-62
lines changed

vlib/v/ast/ast.v

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module ast
55

66
import v.token
77
import v.errors
8+
import v.util
89
import v.pref
910

1011
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
@@ -1924,6 +1925,8 @@ pub:
19241925
is_compile_value bool // $d(...)
19251926
env_pos token.Pos
19261927
is_pkgconfig bool
1928+
mut:
1929+
is_d_resolved bool
19271930
pub mut:
19281931
vweb_tmpl File
19291932
left Expr
@@ -1937,6 +1940,31 @@ pub mut:
19371940
or_block OrExpr
19381941
}
19391942

1943+
// resolve_compile_value resolves the value and return type of `$d()` calls.
1944+
// The result is stored in fields `compile_value` and `result_type`.
1945+
// The argument `compile_values` is expected to be the `Preferences.compile_values` field.
1946+
pub fn (mut cc ComptimeCall) resolve_compile_value(compile_values map[string]string) ! {
1947+
if cc.is_d_resolved {
1948+
return
1949+
}
1950+
if !cc.is_compile_value {
1951+
return error('ComptimeCall is not \$d()')
1952+
}
1953+
arg := cc.args[0] or {
1954+
return error('\$d() takes two arguments, a string and a primitive literal')
1955+
}
1956+
if !arg.expr.is_pure_literal() {
1957+
return error('\$d() values can only be pure literals')
1958+
}
1959+
typ := arg.expr.get_pure_type()
1960+
arg_as_string := arg.str().trim('`"\'')
1961+
value := compile_values[cc.args_var] or { arg_as_string }
1962+
validate_type_string_is_pure_literal(typ, value) or { return error(err.msg()) }
1963+
cc.compile_value = value
1964+
cc.result_type = typ
1965+
cc.is_d_resolved = true
1966+
}
1967+
19401968
pub struct None {
19411969
pub:
19421970
pos token.Pos
@@ -2530,3 +2558,35 @@ pub fn type_can_start_with_token(tok &token.Token) bool {
25302558
}
25312559
}
25322560
}
2561+
2562+
// validate_type_string_is_pure_literal returns `Error` if `str` can not be converted
2563+
// to pure literal `typ` (`i64`, `f64`, `bool`, `char` or `string`).
2564+
pub fn validate_type_string_is_pure_literal(typ Type, str string) ! {
2565+
if typ == bool_type {
2566+
if !(str == 'true' || str == 'false') {
2567+
return error('bool literal `true` or `false` expected, found "${str}"')
2568+
}
2569+
} else if typ == char_type {
2570+
if str.starts_with('\\') {
2571+
if str.len <= 1 {
2572+
return error('empty escape sequence found')
2573+
}
2574+
if !util.is_escape_sequence(str[1]) {
2575+
return error('char literal escape sequence expected, found "${str}"')
2576+
}
2577+
} else if str.len != 1 {
2578+
return error('char literal expected, found "${str}"')
2579+
}
2580+
} else if typ == f64_type {
2581+
if str.count('.') != 1 {
2582+
return error('f64 literal expected, found "${str}"')
2583+
}
2584+
} else if typ == string_type {
2585+
} else if typ == i64_type {
2586+
if !str.is_int() {
2587+
return error('i64 literal expected, found "${str}"')
2588+
}
2589+
} else {
2590+
return error('expected pure literal, found "${str}"')
2591+
}
2592+
}

vlib/v/checker/comptime.v

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,11 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
3030
return ast.string_type
3131
}
3232
if node.is_compile_value {
33-
arg := node.args[0] or {
34-
c.error('\$d() takes two arguments, a string and a primitive literal', node.pos)
35-
return ast.void_type
36-
}
37-
if !arg.expr.is_pure_literal() {
38-
c.error('-d values can only be pure literals', node.pos)
39-
return ast.void_type
40-
}
41-
typ := arg.expr.get_pure_type()
42-
arg_as_string := arg.str().trim('`"\'')
43-
value := c.pref.compile_values[node.args_var] or { arg_as_string }
44-
validate_type_string_is_pure_literal(typ, value) or {
33+
node.resolve_compile_value(c.pref.compile_values) or {
4534
c.error(err.msg(), node.pos)
4635
return ast.void_type
4736
}
48-
node.compile_value = value
49-
node.result_type = typ
50-
return typ
37+
return node.result_type
5138
}
5239
if node.is_embed {
5340
if node.args.len == 1 {
@@ -589,42 +576,6 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
589576
return none
590577
}
591578

592-
fn validate_type_string_is_pure_literal(typ ast.Type, str string) ! {
593-
if typ == ast.bool_type {
594-
if !(str == 'true' || str == 'false') {
595-
return error('bool literal `true` or `false` expected, found "${str}"')
596-
}
597-
} else if typ == ast.char_type {
598-
if str.starts_with('\\') {
599-
if str.len <= 1 {
600-
return error('empty escape sequence found')
601-
}
602-
if !is_escape_sequence(str[1]) {
603-
return error('char literal escape sequence expected, found "${str}"')
604-
}
605-
} else if str.len != 1 {
606-
return error('char literal expected, found "${str}"')
607-
}
608-
} else if typ == ast.f64_type {
609-
if str.count('.') != 1 {
610-
return error('f64 literal expected, found "${str}"')
611-
}
612-
} else if typ == ast.string_type {
613-
} else if typ == ast.i64_type {
614-
if !str.is_int() {
615-
return error('i64 literal expected, found "${str}"')
616-
}
617-
} else {
618-
return error('expected pure literal, found "${str}"')
619-
}
620-
}
621-
622-
@[inline]
623-
fn is_escape_sequence(c u8) bool {
624-
return c in [`x`, `u`, `e`, `n`, `r`, `t`, `v`, `a`, `f`, `b`, `\\`, `\``, `$`, `@`, `?`, `{`,
625-
`}`, `'`, `"`, `U`]
626-
}
627-
628579
fn (mut c Checker) verify_vweb_params_for_method(node ast.Fn) (bool, int, int) {
629580
margs := node.params.len - 1 // first arg is the receiver/this
630581
// if node.attrs.len == 0 || (node.attrs.len == 1 && node.attrs[0].name == 'post') {

vlib/v/checker/containers.v

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,20 @@ fn (mut c Checker) eval_array_fixed_sizes(mut size_expr ast.Expr, size int, elem
308308
ast.IntegerLiteral {
309309
fixed_size = size_expr.val.int()
310310
}
311+
ast.ComptimeCall {
312+
if size_expr.is_compile_value {
313+
size_expr.resolve_compile_value(c.pref.compile_values) or {
314+
c.error(err.msg(), size_expr.pos)
315+
}
316+
if size_expr.result_type != ast.i64_type {
317+
c.error('value from \$d() can only be positive integers when used as fixed size',
318+
size_expr.pos)
319+
}
320+
fixed_size = size_expr.compile_value.int()
321+
} else {
322+
c.error('only \$d() can be used for fixed size arrays', size_expr.pos)
323+
}
324+
}
311325
ast.CastExpr {
312326
if !size_expr.typ.is_pure_int() {
313327
c.error('only integer types are allowed', size_expr.pos)
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
vlib/v/checker/tests/comptime_value_d_values_can_only_be_pure_literals.vv:1:16: error: -d values can only be pure literals
1+
vlib/v/checker/tests/comptime_value_d_values_can_only_be_pure_literals.vv:1:16: error: $d() values can only be pure literals
22
1 | const my_f32 = $d('my_f32', f32(42.0))
3-
| ~~~~~~~~~~~~~~~~~~~~~~~
3+
| ~~~~~~~~~~~~~~~~~~~~~~~
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
42.0
22
false
33
done
4+
[0, 0, 0, 0]

vlib/v/checker/tests/run/using_comptime_d_value.vv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ fn main() {
88
cv_bool := $d('my_bool', false)
99
println(cv_bool)
1010
println('done')
11+
fsa := [$d('fixed_size', 4)]int{}
12+
println(fsa)
1113
}

vlib/v/gen/c/testdata/use_flag_comptime_values.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
a four
44
true
55
g
6+
[0, 0, 0, 0, 0, 0, 0, 0]

vlib/v/gen/c/testdata/use_flag_comptime_values.vv

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// This file should pass if compiled/run with:
2-
// vtest vflags: -d my_f64=2.0 -d my_i64=3 -d my_string="a four" -d my_bool -d my_char=g
2+
// vtest vflags: -d my_f64=2.0 -d my_i64=3 -d my_string="a four" -d my_bool -d my_char=g -d my_size=8
33
const my_f64 = $d('my_f64', 1.0)
44
const my_i64 = $d('my_i64', 2)
55
const my_string = $d('my_string', 'three')
@@ -12,9 +12,12 @@ fn main() {
1212
assert my_string == 'a four'
1313
assert my_bool == true
1414
assert my_char == `g`
15+
my_fixed_size_array := [$d('my_size', 4)]int{}
16+
assert my_fixed_size_array.len == 8
1517
println(my_f64)
1618
println(my_i64)
1719
println(my_string)
1820
println(my_bool)
1921
println(rune(my_char))
22+
println(my_fixed_size_array)
2023
}

vlib/v/parser/parse_type.v

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ fn (mut p Parser) parse_array_type(expecting token.Kind, is_option bool) ast.Typ
2525
fixed_size = size_expr.val.int()
2626
size_unresolved = false
2727
}
28+
ast.ComptimeCall {
29+
if size_expr.is_compile_value {
30+
size_expr.resolve_compile_value(p.pref.compile_values) or {
31+
p.error_with_pos(err.msg(), size_expr.pos)
32+
}
33+
if size_expr.result_type != ast.i64_type {
34+
p.error_with_pos('value from \$d() can only be positive integers when used as fixed size',
35+
size_expr.pos)
36+
}
37+
fixed_size = size_expr.compile_value.int()
38+
size_unresolved = false
39+
}
40+
}
2841
ast.Ident {
2942
if mut const_field := p.table.global_scope.find_const('${p.mod}.${size_expr.name}') {
3043
if mut const_field.expr is ast.IntegerLiteral {

vlib/v/scanner/scanner.v

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ pub fn (mut s Scanner) ident_string() string {
12971297
u32_escapes_pos << s.pos - 1
12981298
}
12991299
// Unknown escape sequence
1300-
if !is_escape_sequence(c) && !c.is_digit() && c != `\n` {
1300+
if !util.is_escape_sequence(c) && !c.is_digit() && c != `\n` {
13011301
s.error('`${c.ascii_str()}` unknown escape sequence')
13021302
}
13031303
}
@@ -1517,12 +1517,6 @@ fn trim_slash_line_break(s string) string {
15171517
return ret_str
15181518
}
15191519

1520-
@[inline]
1521-
fn is_escape_sequence(c u8) bool {
1522-
return c in [`x`, `u`, `e`, `n`, `r`, `t`, `v`, `a`, `f`, `b`, `\\`, `\``, `$`, `@`, `?`, `{`,
1523-
`}`, `'`, `"`, `U`]
1524-
}
1525-
15261520
/// ident_char is called when a backtick "single-char" is parsed from the code
15271521
/// it is needed because some runes (chars) are written with escape sequences
15281522
/// the string it returns should be a standardized, simplified version of the character
@@ -1643,7 +1637,7 @@ pub fn (mut s Scanner) ident_char() string {
16431637
s.error_with_pos('invalid character literal, use \`\\n\` instead', lspos)
16441638
} else if c.len > len {
16451639
ch := c[c.len - 1]
1646-
if !is_escape_sequence(ch) && !ch.is_digit() {
1640+
if !util.is_escape_sequence(ch) && !ch.is_digit() {
16471641
s.error('`${ch.ascii_str()}` unknown escape sequence')
16481642
}
16491643
}

0 commit comments

Comments
 (0)