From a3ee35af91cfd170ee992dc4c9b7ecaf03d9641b Mon Sep 17 00:00:00 2001 From: lmp Date: Sun, 16 Jun 2024 12:52:15 +0200 Subject: [PATCH] comptime: add `vfmt` support, tests, CI job for `$compile_value` --- .github/workflows/comptime_ci.yml | 51 +++++++++++++++++++ cmd/tools/vast/vast.v | 1 + vlib/v/checker/checker.v | 8 +++ vlib/v/checker/comptime.v | 51 +++++++++++++++++-- .../comptime_const/using_comptime_const.vv | 15 ------ .../default_comptime_values_test.v | 13 +++++ .../comptime_value/parser_errors_1.run.out | 3 ++ .../tests/comptime_value/parser_errors_1.vv | 1 + .../use_flag_comptime_values.vv | 15 ++++++ .../using_comptime_value.run.out | 3 ++ .../comptime_value/using_comptime_value.vv | 11 ++++ vlib/v/fmt/fmt.v | 9 ++++ vlib/v/fmt/tests/comptime_value_keep.vv | 7 +++ vlib/v/gen/c/comptime.v | 3 +- 14 files changed, 172 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/comptime_ci.yml delete mode 100644 vlib/v/checker/tests/comptime_const/using_comptime_const.vv create mode 100644 vlib/v/checker/tests/comptime_value/default_comptime_values_test.v create mode 100644 vlib/v/checker/tests/comptime_value/parser_errors_1.run.out create mode 100644 vlib/v/checker/tests/comptime_value/parser_errors_1.vv create mode 100644 vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv create mode 100644 vlib/v/checker/tests/comptime_value/using_comptime_value.run.out create mode 100644 vlib/v/checker/tests/comptime_value/using_comptime_value.vv create mode 100644 vlib/v/fmt/tests/comptime_value_keep.vv diff --git a/.github/workflows/comptime_ci.yml b/.github/workflows/comptime_ci.yml new file mode 100644 index 00000000000000..78cc6c3c9d4255 --- /dev/null +++ b/.github/workflows/comptime_ci.yml @@ -0,0 +1,51 @@ +name: Time CI + +on: + push: + paths: + - 'vlib/**' + - '**/comptime_ci.yml' + - '!**.md' + pull_request: + paths: + - 'vlib/**' + - '**/comptime_ci.yml' + - '!**.md' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + test-comptime-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build V + run: make + - name: Test -cv values + run: ./v -cv my_f64=2.0 -cv my_int=3 -cv my_string="a four" -cv my_bool=true -cv my_char=g run vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv + - name: Test -compile-value values + run: ./v -compile-value my_f64=2.0 -compile-value my_int=3 -compile-value my_string="a four" -compile-value my_bool=true -compile-value my_char=g run vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv + + test-comptime-macos: + runs-on: macos-14 + steps: + - uses: actions/checkout@v4 + - name: Build V + run: make + - name: Test -cv values + run: ./v -cv my_f64=2.0 -cv my_int=3 -cv my_string="a four" -cv my_bool=true -cv my_char=g run vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv + - name: Test -compile-value values + run: ./v -compile-value my_f64=2.0 -compile-value my_int=3 -compile-value my_string="a four" -compile-value my_bool=true -compile-value my_char=g run vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv + + test-time-windows: + runs-on: windows-2019 + steps: + - uses: actions/checkout@v4 + - name: Build V + run: .\make.bat + - name: Test -cv values + run: ./v -cv my_f64=2.0 -cv my_int=3 -cv my_string="a four" -cv my_bool=true -cv my_char=g run vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv + - name: Test -compile-value values + run: ./v -compile-value my_f64=2.0 -compile-value my_int=3 -compile-value my_string="a four" -compile-value my_bool=true -compile-value my_char=g run vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index f5374757244ebe..21999af3a9d82d 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -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)) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0eb5fff545a1ab..a07896f2053dd4 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2428,6 +2428,10 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { } node.main = env } + if flag.contains('\$compile_value(') { + c.error('\$compile_value is not supported in ${node.kind} yet', node.pos) + return + } 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 +2515,10 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { return } } + if flag.contains('\$compile_value(') { + c.error('\$compile_value is not supported in ${node.kind} yet', node.pos) + return + } for deprecated in ['@VMOD', '@VMODULE', '@VPATH', '@VLIB_PATH'] { if flag.contains(deprecated) { if !flag.contains('@VMODROOT') { diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 20b1a17da55a93..1f855fd429935f 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -31,14 +31,23 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { } if node.is_compile_value { arg := node.args[0] or { - c.error('\$compile_value takes two arguments', node.pos) + c.error('\$compile_value takes two arguments, a string and a primitive literal', + node.pos) + return ast.void_type + } + if !arg.expr.is_pure_literal() { + c.error('-cv/-compile-value values can only be pure literals', node.pos) return ast.void_type } typ := arg.expr.get_pure_type() - value := c.pref.compile_compile_values[node.args_var] or { arg.str() } + arg_as_string := arg.str().trim('`"\'') + value := c.pref.compile_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 { @@ -581,6 +590,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') { diff --git a/vlib/v/checker/tests/comptime_const/using_comptime_const.vv b/vlib/v/checker/tests/comptime_const/using_comptime_const.vv deleted file mode 100644 index 29c50f9906c026..00000000000000 --- a/vlib/v/checker/tests/comptime_const/using_comptime_const.vv +++ /dev/null @@ -1,15 +0,0 @@ -//#flag -I $const('my_const','some_value')/xyz -//#include "$const('my_const','some_value')/stdio.h" - -const my_int = $compile_value('my_int',4) -const my_string = $compile_value('my_string','five') - -fn main() { - println(my_int) - println(my_string) - int_compile_value := $compile_value('my_const',5) - println(int_compile_value) - float_compile_value := $compile_value('my_float',1.0) - println(float_compile_value) - println('done') -} diff --git a/vlib/v/checker/tests/comptime_value/default_comptime_values_test.v b/vlib/v/checker/tests/comptime_value/default_comptime_values_test.v new file mode 100644 index 00000000000000..797941922aaabd --- /dev/null +++ b/vlib/v/checker/tests/comptime_value/default_comptime_values_test.v @@ -0,0 +1,13 @@ +const my_f64 = $compile_value('my_f64', 1.0) +const my_int = $compile_value('my_int', 2) +const my_string = $compile_value('my_string', 'three') +const my_bool = $compile_value('my_bool', false) +const my_char = $compile_value('my_char', `f`) + +fn test_default_compile_values() { + assert my_f64 == 1.0 + assert my_int == 2 + assert my_string == 'three' + assert my_bool == false + assert my_char == `f` +} diff --git a/vlib/v/checker/tests/comptime_value/parser_errors_1.run.out b/vlib/v/checker/tests/comptime_value/parser_errors_1.run.out new file mode 100644 index 00000000000000..aa2447e5828a08 --- /dev/null +++ b/vlib/v/checker/tests/comptime_value/parser_errors_1.run.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/comptime_value/parser_errors_1.vv:1:16: error: -cv/-compile-value values can only be pure literals + 1 | const my_f32 = $compile_value('my_f32', f32(42.0)) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/vlib/v/checker/tests/comptime_value/parser_errors_1.vv b/vlib/v/checker/tests/comptime_value/parser_errors_1.vv new file mode 100644 index 00000000000000..a142be5b2f8853 --- /dev/null +++ b/vlib/v/checker/tests/comptime_value/parser_errors_1.vv @@ -0,0 +1 @@ +const my_f32 = $compile_value('my_f32', f32(42.0)) diff --git a/vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv b/vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv new file mode 100644 index 00000000000000..93457e425343b4 --- /dev/null +++ b/vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv @@ -0,0 +1,15 @@ +// This file should pass if compiled/run with: +// v -cv my_f64=2.0 -cv my_int=3 -cv my_string="a four" -cv my_bool=true -cv my_char=g run vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv +const my_f64 = $compile_value('my_f64', 1.0) +const my_int = $compile_value('my_int', 2) +const my_string = $compile_value('my_string', 'three') +const my_bool = $compile_value('my_bool', false) +const my_char = $compile_value('my_char', `f`) + +fn main() { + assert my_f64 == 2.0 + assert my_int == 3 + assert my_string == 'a four' + assert my_bool == true + assert my_char == `g` +} diff --git a/vlib/v/checker/tests/comptime_value/using_comptime_value.run.out b/vlib/v/checker/tests/comptime_value/using_comptime_value.run.out new file mode 100644 index 00000000000000..a172d30507770b --- /dev/null +++ b/vlib/v/checker/tests/comptime_value/using_comptime_value.run.out @@ -0,0 +1,3 @@ +42.0 +false +done \ No newline at end of file diff --git a/vlib/v/checker/tests/comptime_value/using_comptime_value.vv b/vlib/v/checker/tests/comptime_value/using_comptime_value.vv new file mode 100644 index 00000000000000..4bf78c73c3b0af --- /dev/null +++ b/vlib/v/checker/tests/comptime_value/using_comptime_value.vv @@ -0,0 +1,11 @@ +// TODO: support #flag -I $const('my_flag','flag_value')/xyz +// TODO: support #include "$const('my_include','/usr/include')/stdio.h" + +const my_f64 = $compile_value('my_f64', 42.0) + +fn main() { + println(my_f64) + cv_bool := $compile_value('my_bool', false) + println(cv_bool) + println('done') +} diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index e553382e4eab1f..f5439d803e302a 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -2186,6 +2186,15 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) { f.write("\$${node.method_name}('${node.args_var}')") } } + node.method_name == 'compile_value' { + if node.args_var.contains("'") { + f.write('\$${node.method_name}("${node.args_var}", ') + } else { + f.write("\$${node.method_name}('${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})') diff --git a/vlib/v/fmt/tests/comptime_value_keep.vv b/vlib/v/fmt/tests/comptime_value_keep.vv new file mode 100644 index 00000000000000..2b1dfeca174755 --- /dev/null +++ b/vlib/v/fmt/tests/comptime_value_keep.vv @@ -0,0 +1,7 @@ +fn main() { + val_str := $compile_value('key_str', 'value') + val_f64 := $compile_value('key_f64', 42.0) + val_int := $compile_value('key_int', 56) + val_bool := $compile_value('key_bool', false) + val_char := $compile_value('key_char', `f`) +} diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 0565b8bacf1b86..0e1f477aee090f 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -55,9 +55,10 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) { if node.method_name == 'compile_value' { // $compile_value('some_string',) val := util.cescaped_path(node.compile_value) - // dump(node) 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}') }