diff --git a/doc/docs.md b/doc/docs.md index 9dd0696f3d..7980f8cb6d 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -1622,6 +1622,32 @@ $if debug { If you want an `if` to be evaluated at compile time it must be prefixed with a `$` sign. Right now it can only be used to detect an OS or a `-debug` compilation option. +## Compile time pseudo variables + +V also gives your code access to a set of pseudo string variables, that are substituted at compile time: + +- `@FN` => replaced with the name of the current V function +- `@MOD` => replaced with the name of the current V module +- `@STRUCT` => replaced with the name of the current V struct +- `@FILE` => replaced with the path of the V source file +- `@LINE` => replaced with the V line number where it appears (as a string). +- `@COLUMN` => replaced with the column where it appears (as a string). +- `@VEXE` => replaced with the path to the V compiler +- `@VHASH` => replaced with the shortened commit hash of the V compiler (as a string). +- `@VMOD_FILE` => replaced with the contents of the nearest v.mod file (as a string). + +That allows you to do the following example, useful while debugging/logging/tracing your code: +```v +eprintln( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN) +``` + +Another example, is if you want to embed the version/name from v.mod *inside* your executable: +```v +import v.vmod +vm := vmod.decode( @VMOD_FILE ) or { panic(err) } +eprintln('$vm.name $vm.version\n $vm.description') +``` + ## Reflection via codegen Having built-in JSON support is nice, but V also allows you to create efficient diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index b249f6da9c..a75a976dd5 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -7,6 +7,7 @@ import os import v.token import v.pref import v.util +import v.vmod const ( single_quote = `\'` @@ -32,6 +33,7 @@ pub mut: fn_name string // needed for @FN mod_name string // needed for @MOD struct_name string // needed for @STRUCT + vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path* is_print_line_on_error bool is_print_colored_error bool is_print_rel_paths_on_error bool @@ -745,6 +747,7 @@ pub fn (mut s Scanner) scan() token.Token { // @LINE => will be substituted with the V line number where it appears (as a string). // @COLUMN => will be substituted with the column where it appears (as a string). // @VHASH => will be substituted with the shortened commit hash of the V compiler (as a string). + // @VMOD_FILE => will be substituted with the contents of the nearest v.mod file (as a string). // This allows things like this: // println( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN) // ... which is useful while debugging/tracing @@ -773,6 +776,17 @@ pub fn (mut s Scanner) scan() token.Token { if name == 'VHASH' { return s.new_token(.string, util.vhash(), 6) } + if name == 'VMOD_FILE' { + if s.vmod_file_content.len == 0 { + vmod_file_location := vmod.mod_file_cacher.get( os.dir( os.real_path(s.file_path) ) ) + if vmod_file_location.vmod_file.len == 0 { + s.error('@VMOD_FILE can be used only in projects, that have v.mod file') + } + vmod_content := os.read_file(vmod_file_location.vmod_file) or {''} + s.vmod_file_content = vmod_content + } + return s.new_token(.string, s.vmod_file_content, 10) + } if !token.is_key(name) { s.error('@ must be used before keywords (e.g. `@type string`)') } diff --git a/vlib/v/scanner/scanner_test.v b/vlib/v/scanner/scanner_test.v index 403f031af9..a5d5324278 100644 --- a/vlib/v/scanner/scanner_test.v +++ b/vlib/v/scanner/scanner_test.v @@ -82,3 +82,12 @@ fn test_scan() { } + +fn test_vmod_file() { + content := @VMOD_FILE + assert content.len > 0 + assert content.contains('Module {') + assert content.contains('name:') + assert content.contains('version:') + assert content.contains('description:') +}