mirror of
https://github.com/vlang/v.git
synced 2025-09-19 04:17:46 -04:00
comptime: support -d ident=value
and var := $d('ident', 0)
(#21685)
This commit is contained in:
parent
aaa23bb058
commit
78b77b9f14
@ -1,3 +1,8 @@
|
||||
## V 0.4.7 (NEXT)
|
||||
|
||||
#### Improvements in the language
|
||||
- comptime: add support for `-d ident=value` and retrieval in code via `$d('ident', <default value>)`.
|
||||
|
||||
## V 0.4.6
|
||||
*20 May 2024*
|
||||
|
||||
|
@ -1019,6 +1019,7 @@ fn (t Tree) comptime_call(node ast.ComptimeCall) &Node {
|
||||
obj.add_terse('result_type', t.type_node(node.result_type))
|
||||
obj.add('scope', t.scope(node.scope))
|
||||
obj.add_terse('env_value', t.string_node(node.env_value))
|
||||
obj.add_terse('compile_value', t.string_node(node.compile_value))
|
||||
obj.add('pos', t.pos(node.pos))
|
||||
obj.add_terse('args', t.array_node_call_arg(node.args))
|
||||
obj.add_terse('or_block', t.or_expr(node.or_block))
|
||||
|
61
doc/docs.md
61
doc/docs.md
@ -6120,6 +6120,67 @@ V can bring in values at compile time from environment variables.
|
||||
`$env('ENV_VAR')` can also be used in top-level `#flag` and `#include` statements:
|
||||
`#flag linux -I $env('JAVA_HOME')/include`.
|
||||
|
||||
#### `$d`
|
||||
|
||||
V can bring in values at compile time from `-d ident=value` flag defines, passed on
|
||||
the command line to the compiler. You can also pass `-d ident`, which will have the
|
||||
same meaning as passing `-d ident=true`.
|
||||
|
||||
To get the value in your code, use: `$d('ident', default)`, where `default`
|
||||
can be `false` for booleans, `0` or `123` for i64 numbers, `0.0` or `113.0`
|
||||
for f64 numbers, `'a string'` for strings.
|
||||
|
||||
When a flag is not provided via the command line, `$d()` will return the `default`
|
||||
value provided as the *second* argument.
|
||||
|
||||
```v
|
||||
module main
|
||||
|
||||
const my_i64 = $d('my_i64', 1024)
|
||||
|
||||
fn main() {
|
||||
compile_time_value := $d('my_string', 'V')
|
||||
println(compile_time_value)
|
||||
println(my_i64)
|
||||
}
|
||||
```
|
||||
|
||||
Running the above with `v run .` will output:
|
||||
```
|
||||
V
|
||||
1024
|
||||
```
|
||||
|
||||
Running the above with `v -d my_i64=4096 -d my_string="V rocks" run .` will output:
|
||||
```
|
||||
V rocks
|
||||
4096
|
||||
```
|
||||
|
||||
Here is an example of how to use the default values, which have to be *pure* literals:
|
||||
```v
|
||||
fn main() {
|
||||
val_str := $d('id_str', 'value') // can be changed by providing `-d id_str="my id"`
|
||||
val_f64 := $d('id_f64', 42.0) // can be changed by providing `-d id_f64=84.0`
|
||||
val_i64 := $d('id_i64', 56) // can be changed by providing `-d id_i64=123`
|
||||
val_bool := $d('id_bool', false) // can be changed by providing `-d id_bool=true`
|
||||
val_char := $d('id_char', `f`) // can be changed by providing `-d id_char=v`
|
||||
println(val_str)
|
||||
println(val_f64)
|
||||
println(val_i64)
|
||||
println(val_bool)
|
||||
println(rune(val_char))
|
||||
}
|
||||
```
|
||||
|
||||
`$d('ident','value')` can also be used in top-level statements like `#flag` and `#include`:
|
||||
`#flag linux -I $d('my_include','/usr')/include`. The default value for `$d` when used in these
|
||||
statements should be literal `string`s.
|
||||
|
||||
`$d('ident', false)` can also be used inside `$if $d('ident', false) {` statements,
|
||||
granting you the ability to selectively turn on/off certain sections of code, at compile
|
||||
time, without modifying your source code, or keeping different versions of it.
|
||||
|
||||
#### `$compile_error` and `$compile_warn`
|
||||
|
||||
These two comptime functions are very useful for displaying custom errors/warnings during
|
||||
|
@ -1913,26 +1913,28 @@ pub mut:
|
||||
@[minify]
|
||||
pub struct ComptimeCall {
|
||||
pub:
|
||||
pos token.Pos
|
||||
has_parens bool // if $() is used, for vfmt
|
||||
method_name string
|
||||
method_pos token.Pos
|
||||
scope &Scope = unsafe { nil }
|
||||
is_vweb bool
|
||||
is_embed bool
|
||||
is_env bool
|
||||
env_pos token.Pos
|
||||
is_pkgconfig bool
|
||||
pos token.Pos
|
||||
has_parens bool // if $() is used, for vfmt
|
||||
method_name string
|
||||
method_pos token.Pos
|
||||
scope &Scope = unsafe { nil }
|
||||
is_vweb bool
|
||||
is_embed bool // $embed_file(...)
|
||||
is_env bool // $env(...) // TODO: deprecate after $d() is stable
|
||||
is_compile_value bool // $d(...)
|
||||
env_pos token.Pos
|
||||
is_pkgconfig bool
|
||||
pub mut:
|
||||
vweb_tmpl File
|
||||
left Expr
|
||||
left_type Type
|
||||
result_type Type
|
||||
env_value string
|
||||
args_var string
|
||||
args []CallArg
|
||||
embed_file EmbeddedFile
|
||||
or_block OrExpr
|
||||
vweb_tmpl File
|
||||
left Expr
|
||||
left_type Type
|
||||
result_type Type
|
||||
env_value string
|
||||
compile_value string
|
||||
args_var string
|
||||
args []CallArg
|
||||
embed_file EmbeddedFile
|
||||
or_block OrExpr
|
||||
}
|
||||
|
||||
pub struct None {
|
||||
|
@ -2428,6 +2428,13 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
||||
}
|
||||
node.main = env
|
||||
}
|
||||
if flag.contains('\$d(') {
|
||||
d := util.resolve_d_value(c.pref.compile_values, flag) or {
|
||||
c.error(err.msg(), node.pos)
|
||||
return
|
||||
}
|
||||
node.main = d
|
||||
}
|
||||
flag_no_comment := flag.all_before('//').trim_space()
|
||||
if node.kind == 'include' || node.kind == 'preinclude' {
|
||||
if !((flag_no_comment.starts_with('"') && flag_no_comment.ends_with('"'))
|
||||
@ -2511,6 +2518,12 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if flag.contains('\$d(') {
|
||||
flag = util.resolve_d_value(c.pref.compile_values, flag) or {
|
||||
c.error(err.msg(), node.pos)
|
||||
return
|
||||
}
|
||||
}
|
||||
for deprecated in ['@VMOD', '@VMODULE', '@VPATH', '@VLIB_PATH'] {
|
||||
if flag.contains(deprecated) {
|
||||
if !flag.contains('@VMODROOT') {
|
||||
|
@ -29,6 +29,26 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
|
||||
node.env_value = env_value
|
||||
return ast.string_type
|
||||
}
|
||||
if node.is_compile_value {
|
||||
arg := node.args[0] or {
|
||||
c.error('\$d() takes two arguments, a string and a primitive literal', node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
if !arg.expr.is_pure_literal() {
|
||||
c.error('-d values can only be pure literals', node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
typ := arg.expr.get_pure_type()
|
||||
arg_as_string := arg.str().trim('`"\'')
|
||||
value := c.pref.compile_values[node.args_var] or { arg_as_string }
|
||||
validate_type_string_is_pure_literal(typ, value) or {
|
||||
c.error(err.msg(), node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
node.compile_value = value
|
||||
node.result_type = typ
|
||||
return typ
|
||||
}
|
||||
if node.is_embed {
|
||||
if node.args.len == 1 {
|
||||
embed_arg := node.args[0]
|
||||
@ -569,6 +589,42 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
|
||||
return none
|
||||
}
|
||||
|
||||
fn validate_type_string_is_pure_literal(typ ast.Type, str string) ! {
|
||||
if typ == ast.bool_type {
|
||||
if !(str == 'true' || str == 'false') {
|
||||
return error('bool literal `true` or `false` expected, found "${str}"')
|
||||
}
|
||||
} else if typ == ast.char_type {
|
||||
if str.starts_with('\\') {
|
||||
if str.len <= 1 {
|
||||
return error('empty escape sequence found')
|
||||
}
|
||||
if !is_escape_sequence(str[1]) {
|
||||
return error('char literal escape sequence expected, found "${str}"')
|
||||
}
|
||||
} else if str.len != 1 {
|
||||
return error('char literal expected, found "${str}"')
|
||||
}
|
||||
} else if typ == ast.f64_type {
|
||||
if str.count('.') != 1 {
|
||||
return error('f64 literal expected, found "${str}"')
|
||||
}
|
||||
} else if typ == ast.string_type {
|
||||
} else if typ == ast.i64_type {
|
||||
if !str.is_int() {
|
||||
return error('i64 literal expected, found "${str}"')
|
||||
}
|
||||
} else {
|
||||
return error('expected pure literal, found "${str}"')
|
||||
}
|
||||
}
|
||||
|
||||
@[inline]
|
||||
fn is_escape_sequence(c u8) bool {
|
||||
return c in [`x`, `u`, `e`, `n`, `r`, `t`, `v`, `a`, `f`, `b`, `\\`, `\``, `$`, `@`, `?`, `{`,
|
||||
`}`, `'`, `"`, `U`]
|
||||
}
|
||||
|
||||
fn (mut c Checker) verify_vweb_params_for_method(node ast.Fn) (bool, int, int) {
|
||||
margs := node.params.len - 1 // first arg is the receiver/this
|
||||
// if node.attrs.len == 0 || (node.attrs.len == 1 && node.attrs[0].name == 'post') {
|
||||
@ -969,6 +1025,16 @@ fn (mut c Checker) comptime_if_branch(mut cond ast.Expr, pos token.Pos) Comptime
|
||||
return .skip
|
||||
}
|
||||
m.run() or { return .skip }
|
||||
return .eval
|
||||
}
|
||||
if cond.is_compile_value {
|
||||
t := c.expr(mut cond)
|
||||
if t != ast.bool_type {
|
||||
c.error('inside \$if, only \$d() expressions that return bool are allowed',
|
||||
cond.pos)
|
||||
return .skip
|
||||
}
|
||||
return .unknown // always fully generate the code for that branch
|
||||
}
|
||||
return .eval
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
builder error: Header file "/opt/invalid/include/stdio.h", needed for module `main` was not found. Please install the corresponding development headers.
|
@ -0,0 +1 @@
|
||||
#include "$d('my_include','/opt/invalid/include')/stdio.h"
|
@ -0,0 +1,3 @@
|
||||
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 | const my_f32 = $d('my_f32', f32(42.0))
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
@ -0,0 +1 @@
|
||||
const my_f32 = $d('my_f32', f32(42.0))
|
3
vlib/v/checker/tests/run/using_comptime_d_value.run.out
Normal file
3
vlib/v/checker/tests/run/using_comptime_d_value.run.out
Normal file
@ -0,0 +1,3 @@
|
||||
42.0
|
||||
false
|
||||
done
|
11
vlib/v/checker/tests/run/using_comptime_d_value.vv
Normal file
11
vlib/v/checker/tests/run/using_comptime_d_value.vv
Normal file
@ -0,0 +1,11 @@
|
||||
#flag -I $d('my_flag','flag_value')/xyz
|
||||
#include "@VMODROOT/$d('my_include','vlib/v')/tests/project_with_c_code/mod1/c/header.h"
|
||||
|
||||
const my_f64 = $d('my_f64', 42.0)
|
||||
|
||||
fn main() {
|
||||
println(my_f64)
|
||||
cv_bool := $d('my_bool', false)
|
||||
println(cv_bool)
|
||||
println('done')
|
||||
}
|
@ -237,6 +237,7 @@ fn (mut tasks Tasks) run() {
|
||||
// cleaner error message, than a generic C error, but without the explanation.
|
||||
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_1.vv'
|
||||
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv'
|
||||
m_skip_files << 'vlib/v/checker/tests/comptime_value_d_in_include_errors.vv'
|
||||
}
|
||||
$if msvc {
|
||||
m_skip_files << 'vlib/v/checker/tests/asm_alias_does_not_exist.vv'
|
||||
@ -244,6 +245,7 @@ fn (mut tasks Tasks) run() {
|
||||
// TODO: investigate why MSVC regressed
|
||||
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_1.vv'
|
||||
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv'
|
||||
m_skip_files << 'vlib/v/checker/tests/comptime_value_d_in_include_errors.vv'
|
||||
}
|
||||
$if windows {
|
||||
m_skip_files << 'vlib/v/checker/tests/modules/deprecated_module'
|
||||
|
@ -2186,6 +2186,11 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) {
|
||||
f.write("\$${node.method_name}('${node.args_var}')")
|
||||
}
|
||||
}
|
||||
node.method_name == 'd' {
|
||||
f.write("\$d('${node.args_var}', ")
|
||||
f.expr(node.args[0].expr)
|
||||
f.write(')')
|
||||
}
|
||||
node.method_name == 'res' {
|
||||
if node.args_var != '' {
|
||||
f.write('\$res(${node.args_var})')
|
||||
|
7
vlib/v/fmt/tests/comptime_value_keep.vv
Normal file
7
vlib/v/fmt/tests/comptime_value_keep.vv
Normal file
@ -0,0 +1,7 @@
|
||||
fn main() {
|
||||
val_str := $d('key_str', 'value')
|
||||
val_f64 := $d('key_f64', 42.0)
|
||||
val_int := $d('key_int', 56)
|
||||
val_bool := $d('key_bool', false)
|
||||
val_char := $d('key_char', `f`)
|
||||
}
|
@ -48,10 +48,23 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
|
||||
}
|
||||
if node.method_name == 'env' {
|
||||
// $env('ENV_VAR_NAME')
|
||||
// TODO: deprecate after support for $d() is stable
|
||||
val := util.cescaped_path(os.getenv(node.args_var))
|
||||
g.write('_SLIT("${val}")')
|
||||
return
|
||||
}
|
||||
if node.method_name == 'd' {
|
||||
// $d('some_string',<default value>), affected by `-d some_string=actual_value`
|
||||
val := util.cescaped_path(node.compile_value)
|
||||
if node.result_type == ast.string_type {
|
||||
g.write('_SLIT("${val}")')
|
||||
} else if node.result_type == ast.char_type {
|
||||
g.write("'${val}'")
|
||||
} else {
|
||||
g.write('${val}')
|
||||
}
|
||||
return
|
||||
}
|
||||
if node.method_name == 'res' {
|
||||
if node.args_var != '' {
|
||||
g.write('${g.defer_return_tmp_var}.arg${node.args_var}')
|
||||
@ -677,7 +690,22 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) (bool, bool) {
|
||||
return true, false
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
g.write('${pkg_exist}')
|
||||
if cond.method_name == 'pkgconfig' {
|
||||
g.write('${pkg_exist}')
|
||||
return true, false
|
||||
}
|
||||
if cond.method_name == 'd' {
|
||||
if cond.result_type == ast.bool_type {
|
||||
if cond.compile_value == 'true' {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
} else {
|
||||
g.write('defined(CUSTOM_DEFINE_${cond.args_var})')
|
||||
}
|
||||
return true, false
|
||||
}
|
||||
return true, false
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
|
5
vlib/v/gen/c/testdata/use_flag_comptime_values.out
vendored
Normal file
5
vlib/v/gen/c/testdata/use_flag_comptime_values.out
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
2.0
|
||||
3
|
||||
a four
|
||||
true
|
||||
g
|
20
vlib/v/gen/c/testdata/use_flag_comptime_values.vv
vendored
Normal file
20
vlib/v/gen/c/testdata/use_flag_comptime_values.vv
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// This file should pass if compiled/run with:
|
||||
// vtest vflags: -d my_f64=2.0 -d my_i64=3 -d my_string="a four" -d my_bool -d my_char=g
|
||||
const my_f64 = $d('my_f64', 1.0)
|
||||
const my_i64 = $d('my_i64', 2)
|
||||
const my_string = $d('my_string', 'three')
|
||||
const my_bool = $d('my_bool', false)
|
||||
const my_char = $d('my_char', `f`)
|
||||
|
||||
fn main() {
|
||||
assert my_f64 == 2.0
|
||||
assert my_i64 == 3
|
||||
assert my_string == 'a four'
|
||||
assert my_bool == true
|
||||
assert my_char == `g`
|
||||
println(my_f64)
|
||||
println(my_i64)
|
||||
println(my_string)
|
||||
println(my_bool)
|
||||
println(rune(my_char))
|
||||
}
|
44
vlib/v/gen/c/testdata/use_flag_comptime_with_if.out
vendored
Normal file
44
vlib/v/gen/c/testdata/use_flag_comptime_with_if.out
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
>>>>> Should be compiled with: `./v -d ddd1=true -d ddd2=false`
|
||||
>>>>> Note that ddd3 is not passed *on purpose*, to test the default .
|
||||
===========================================================================
|
||||
$d(ddd1, false) : true
|
||||
$d(ddd1, true) : true
|
||||
------------------------------------------------------------
|
||||
Checking $if with default false:
|
||||
1 ddd1 is true
|
||||
Checking $if with default true:
|
||||
2 ddd1 is true
|
||||
------------------------------------------------------------
|
||||
Checking $if !$d(ddd1) with default false:
|
||||
3 !ddd1 is false
|
||||
Checking $if !$d() with default true:
|
||||
4 !ddd1 is false
|
||||
|
||||
===========================================================================
|
||||
$d(ddd2, false) : false
|
||||
$d(ddd2, true) : false
|
||||
------------------------------------------------------------
|
||||
Checking $if with default false:
|
||||
1 ddd2 is false
|
||||
Checking $if with default true:
|
||||
2 ddd2 is false
|
||||
------------------------------------------------------------
|
||||
Checking $if !$d() with default false:
|
||||
3 !ddd2 is true
|
||||
Checking $if !$d() with default true:
|
||||
4 !ddd2 is true
|
||||
|
||||
===========================================================================
|
||||
$d(ddd3, false) : false
|
||||
$d(ddd3, true) : true
|
||||
------------------------------------------------------------
|
||||
Checking $if with default false:
|
||||
1 ddd3 is false
|
||||
Checking $if with default true:
|
||||
2 ddd3 is true
|
||||
------------------------------------------------------------
|
||||
Checking $if !$d() with default false:
|
||||
3 !ddd3 is true
|
||||
Checking $if !$d() with default true:
|
||||
4 !ddd3 is false
|
||||
|
111
vlib/v/gen/c/testdata/use_flag_comptime_with_if.vv
vendored
Normal file
111
vlib/v/gen/c/testdata/use_flag_comptime_with_if.vv
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
// vtest vflags: -d ddd1=true -d ddd2=false
|
||||
|
||||
fn main() {
|
||||
println('>>>>> Should be compiled with: `./v -d ddd1=true -d ddd2=false`')
|
||||
println('>>>>> Note that ddd3 is not passed *on purpose*, to test the default .')
|
||||
assert $d('ddd1', false) == true
|
||||
assert $d('ddd2', true) == false
|
||||
assert $d('ddd3', true) == true
|
||||
check_ddd1()
|
||||
check_ddd2()
|
||||
check_ddd3()
|
||||
}
|
||||
|
||||
fn check_ddd1() {
|
||||
println('===========================================================================')
|
||||
println('\$d(ddd1, false) : ' + $d('ddd1', false).str())
|
||||
println('\$d(ddd1, true) : ' + $d('ddd1', true).str())
|
||||
println('------------------------------------------------------------')
|
||||
println('Checking \$if with default false:')
|
||||
$if $d('ddd1', false) {
|
||||
println('1 ddd1 is true')
|
||||
} $else {
|
||||
println('1 ddd1 is false')
|
||||
}
|
||||
println('Checking \$if with default true:')
|
||||
$if $d('ddd1', true) {
|
||||
println('2 ddd1 is true')
|
||||
} $else {
|
||||
println('2 ddd1 is false')
|
||||
}
|
||||
println('------------------------------------------------------------')
|
||||
println('Checking \$if !\$d(ddd1) with default false:')
|
||||
$if !$d('ddd1', false) {
|
||||
println('3 !ddd1 is true')
|
||||
} $else {
|
||||
println('3 !ddd1 is false')
|
||||
}
|
||||
println('Checking \$if !\$d() with default true:')
|
||||
$if !$d('ddd1', true) {
|
||||
println('4 !ddd1 is true')
|
||||
} $else {
|
||||
println('4 !ddd1 is false')
|
||||
}
|
||||
println('')
|
||||
}
|
||||
|
||||
fn check_ddd2() {
|
||||
println('===========================================================================')
|
||||
println('\$d(ddd2, false) : ' + $d('ddd2', false).str())
|
||||
println('\$d(ddd2, true) : ' + $d('ddd2', true).str())
|
||||
println('------------------------------------------------------------')
|
||||
println('Checking \$if with default false:')
|
||||
$if $d('ddd2', false) {
|
||||
println('1 ddd2 is true')
|
||||
} $else {
|
||||
println('1 ddd2 is false')
|
||||
}
|
||||
println('Checking \$if with default true:')
|
||||
$if $d('ddd2', true) {
|
||||
println('2 ddd2 is true')
|
||||
} $else {
|
||||
println('2 ddd2 is false')
|
||||
}
|
||||
println('------------------------------------------------------------')
|
||||
println('Checking \$if !\$d() with default false:')
|
||||
$if !$d('ddd2', false) {
|
||||
println('3 !ddd2 is true')
|
||||
} $else {
|
||||
println('3 !ddd2 is false')
|
||||
}
|
||||
println('Checking \$if !\$d() with default true:')
|
||||
$if !$d('ddd2', true) {
|
||||
println('4 !ddd2 is true')
|
||||
} $else {
|
||||
println('4 !ddd2 is false')
|
||||
}
|
||||
println('')
|
||||
}
|
||||
|
||||
fn check_ddd3() {
|
||||
println('===========================================================================')
|
||||
println('\$d(ddd3, false) : ' + $d('ddd3', false).str())
|
||||
println('\$d(ddd3, true) : ' + $d('ddd3', true).str())
|
||||
println('------------------------------------------------------------')
|
||||
println('Checking \$if with default false:')
|
||||
$if $d('ddd3', false) {
|
||||
println('1 ddd3 is true')
|
||||
} $else {
|
||||
println('1 ddd3 is false')
|
||||
}
|
||||
println('Checking \$if with default true:')
|
||||
$if $d('ddd3', true) {
|
||||
println('2 ddd3 is true')
|
||||
} $else {
|
||||
println('2 ddd3 is false')
|
||||
}
|
||||
println('------------------------------------------------------------')
|
||||
println('Checking \$if !\$d() with default false:')
|
||||
$if !$d('ddd3', false) {
|
||||
println('3 !ddd3 is true')
|
||||
} $else {
|
||||
println('3 !ddd3 is false')
|
||||
}
|
||||
println('Checking \$if !\$d() with default true:')
|
||||
$if !$d('ddd3', true) {
|
||||
println('4 !ddd3 is true')
|
||||
} $else {
|
||||
println('4 !ddd3 is false')
|
||||
}
|
||||
println('')
|
||||
}
|
@ -8,7 +8,7 @@ import v.ast
|
||||
import v.token
|
||||
|
||||
const supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig', 'compile_error',
|
||||
'compile_warn', 'res']
|
||||
'compile_warn', 'd', 'res']
|
||||
const comptime_types = ['map', 'array', 'array_dynamic', 'array_fixed', 'int', 'float', 'struct',
|
||||
'interface', 'enum', 'sumtype', 'alias', 'function', 'option', 'string']
|
||||
|
||||
@ -106,7 +106,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
||||
}
|
||||
start_pos := p.tok.pos()
|
||||
p.check(.dollar)
|
||||
error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()`, `\$vweb.html()`, `\$compile_error()`, `\$compile_warn()` and `\$res()` comptime functions are supported right now'
|
||||
error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()`, `\$vweb.html()`, `\$compile_error()`, `\$compile_warn()`, `\$d()` and `\$res()` comptime functions are supported right now'
|
||||
if p.peek_tok.kind == .dot {
|
||||
name := p.check_name() // skip `vweb.html()` TODO
|
||||
if name != 'vweb' && name != 'veb' {
|
||||
@ -122,7 +122,6 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
||||
}
|
||||
is_embed_file := method_name == 'embed_file'
|
||||
is_html := method_name == 'html'
|
||||
// $env('ENV_VAR_NAME')
|
||||
p.check(.lpar)
|
||||
arg_pos := p.tok.pos()
|
||||
if method_name in ['env', 'pkgconfig', 'compile_error', 'compile_warn'] {
|
||||
@ -160,6 +159,27 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
||||
method_name: method_name
|
||||
pos: start_pos.extend(p.prev_tok.pos())
|
||||
}
|
||||
} else if method_name == 'd' {
|
||||
const_string := p.tok.lit
|
||||
// const_name_pos := p.tok.pos()
|
||||
p.check(.string)
|
||||
p.check(.comma)
|
||||
arg_expr := p.expr(0)
|
||||
args := [
|
||||
ast.CallArg{
|
||||
expr: arg_expr
|
||||
pos: p.tok.pos()
|
||||
},
|
||||
]
|
||||
p.check(.rpar)
|
||||
return ast.ComptimeCall{
|
||||
scope: unsafe { nil }
|
||||
is_compile_value: true
|
||||
method_name: method_name
|
||||
args_var: const_string
|
||||
args: args
|
||||
pos: start_pos.extend(p.prev_tok.pos())
|
||||
}
|
||||
}
|
||||
has_string_arg := p.tok.kind == .string
|
||||
mut literal_string_param := if is_html && !has_string_arg { '' } else { p.tok.lit }
|
||||
|
@ -193,6 +193,7 @@ pub mut:
|
||||
// -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another ? { will NOT get here }`
|
||||
compile_defines []string // just ['vfmt']
|
||||
compile_defines_all []string // contains both: ['vfmt','another']
|
||||
compile_values map[string]string // the map will contain for `-d key=value`: compile_values['key'] = 'value', and for `-d ident`, it will be: compile_values['ident'] = 'true'
|
||||
//
|
||||
run_args []string // `v run x.v 1 2 3` => `1 2 3`
|
||||
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
|
||||
@ -1170,39 +1171,37 @@ pub fn cc_from_string(s string) CompilerType {
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut prefs Preferences) parse_compile_value(define string) {
|
||||
if !define.contains('=') {
|
||||
eprintln_exit('V error: Define argument value missing for ${define}.')
|
||||
return
|
||||
}
|
||||
name := define.all_before('=')
|
||||
value := define.all_after_first('=')
|
||||
prefs.compile_values[name] = value
|
||||
}
|
||||
|
||||
fn (mut prefs Preferences) parse_define(define string) {
|
||||
define_parts := define.split('=')
|
||||
prefs.diagnose_deprecated_defines(define_parts)
|
||||
if !(prefs.is_debug && define == 'debug') {
|
||||
prefs.build_options << '-d ${define}'
|
||||
}
|
||||
if define_parts.len == 1 {
|
||||
if !define.contains('=') {
|
||||
prefs.compile_values[define] = 'true'
|
||||
prefs.compile_defines << define
|
||||
prefs.compile_defines_all << define
|
||||
return
|
||||
}
|
||||
if define_parts.len == 2 {
|
||||
prefs.compile_defines_all << define_parts[0]
|
||||
match define_parts[1] {
|
||||
'0' {}
|
||||
'1' {
|
||||
prefs.compile_defines << define_parts[0]
|
||||
}
|
||||
else {
|
||||
eprintln_exit(
|
||||
'V error: Unknown define argument value `${define_parts[1]}` for ${define_parts[0]}.' +
|
||||
' Expected `0` or `1`.')
|
||||
}
|
||||
dname := define.all_before('=')
|
||||
dvalue := define.all_after_first('=')
|
||||
prefs.compile_values[dname] = dvalue
|
||||
prefs.compile_defines_all << dname
|
||||
match dvalue {
|
||||
'0' {}
|
||||
'1' {
|
||||
prefs.compile_defines << dname
|
||||
}
|
||||
return
|
||||
else {}
|
||||
}
|
||||
eprintln_exit('V error: Unknown define argument: ${define}. Expected at most one `=`.')
|
||||
}
|
||||
|
||||
fn (mut prefs Preferences) diagnose_deprecated_defines(define_parts []string) {
|
||||
// if define_parts[0] == 'no_bounds_checking' {
|
||||
// eprintln('`-d no_bounds_checking` was deprecated in 2022/10/30. Use `-no-bounds-checking` instead.')
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn supported_test_runners_list() string {
|
||||
|
13
vlib/v/tests/comptime_value_d_default_test.v
Normal file
13
vlib/v/tests/comptime_value_d_default_test.v
Normal file
@ -0,0 +1,13 @@
|
||||
const my_f64 = $d('my_f64', 1.0)
|
||||
const my_i64 = $d('my_i64', 2)
|
||||
const my_string = $d('my_string', 'three')
|
||||
const my_bool = $d('my_bool', false)
|
||||
const my_char = $d('my_char', `f`)
|
||||
|
||||
fn test_default_compile_values() {
|
||||
assert my_f64 == 1.0
|
||||
assert my_i64 == 2
|
||||
assert my_string == 'three'
|
||||
assert my_bool == false
|
||||
assert my_char == `f`
|
||||
}
|
@ -115,6 +115,71 @@ pub fn resolve_env_value(str string, check_for_presence bool) !string {
|
||||
return rep
|
||||
}
|
||||
|
||||
const d_sig = "\$d('"
|
||||
|
||||
// resolve_d_value replaces all occurrences of `$d('ident','value')`
|
||||
// in `str` with either the default `'value'` param or a compile value passed via `-d ident=value`.
|
||||
pub fn resolve_d_value(compile_values map[string]string, str string) !string {
|
||||
at := str.index(util.d_sig) or {
|
||||
return error('no "${util.d_sig}' + '...\')" could be found in "${str}".')
|
||||
}
|
||||
mut all_parsed := util.d_sig
|
||||
mut ch := u8(`.`)
|
||||
mut d_ident := ''
|
||||
mut i := 0
|
||||
for i = at + util.d_sig.len; i < str.len && ch != `'`; i++ {
|
||||
ch = u8(str[i])
|
||||
all_parsed += ch.ascii_str()
|
||||
if ch.is_letter() || ch.is_digit() || ch == `_` {
|
||||
d_ident += ch.ascii_str()
|
||||
} else {
|
||||
if !(ch == `'`) {
|
||||
if ch == `$` {
|
||||
return error('cannot use string interpolation in compile time \$d() expression')
|
||||
}
|
||||
return error('invalid `\$d` identifier in "${str}", invalid character "${ch.ascii_str()}"')
|
||||
}
|
||||
}
|
||||
}
|
||||
if d_ident == '' {
|
||||
return error('first argument of `\$d` must be a string identifier')
|
||||
}
|
||||
|
||||
// at this point we should have a valid identifier in `d_ident`.
|
||||
// Next we parse out the default string value
|
||||
|
||||
// advance past the `,` and the opening `'` in second argument, or ... eat whatever is there
|
||||
all_parsed += u8(str[i]).ascii_str()
|
||||
i++
|
||||
all_parsed += u8(str[i]).ascii_str()
|
||||
i++
|
||||
// Rinse, repeat for the expected default value string
|
||||
ch = u8(`.`)
|
||||
mut d_default_value := ''
|
||||
for ; i < str.len && ch != `'`; i++ {
|
||||
ch = u8(str[i])
|
||||
all_parsed += ch.ascii_str()
|
||||
if !(ch == `'`) {
|
||||
d_default_value += ch.ascii_str()
|
||||
}
|
||||
if ch == `$` {
|
||||
return error('cannot use string interpolation in compile time \$d() expression')
|
||||
}
|
||||
}
|
||||
if d_default_value == '' {
|
||||
return error('second argument of `\$d` must be a pure literal')
|
||||
}
|
||||
// at this point we have the identifier and the default value.
|
||||
// now we need to resolve which one to use from `compile_values`.
|
||||
d_value := compile_values[d_ident] or { d_default_value }
|
||||
// if more `$d()` calls remains, resolve those as well:
|
||||
rep := str.replace_once(all_parsed + ')', d_value)
|
||||
if rep.contains(util.d_sig) {
|
||||
return resolve_d_value(compile_values, rep)
|
||||
}
|
||||
return rep
|
||||
}
|
||||
|
||||
// launch_tool - starts a V tool in a separate process, passing it the `args`.
|
||||
// All V tools are located in the cmd/tools folder, in files or folders prefixed by
|
||||
// the letter `v`, followed by the tool name, i.e. `cmd/tools/vdoc/` or `cmd/tools/vpm.v`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user