Skip to content

Commit

Permalink
comptime: add vfmt support, tests, CI job for $compile_value
Browse files Browse the repository at this point in the history
  • Loading branch information
larpon committed Jun 16, 2024
1 parent 3998710 commit a3ee35a
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 19 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/comptime_ci.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions cmd/tools/vast/vast.v
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
8 changes: 8 additions & 0 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -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('"'))
Expand Down Expand Up @@ -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') {
Expand Down
51 changes: 48 additions & 3 deletions vlib/v/checker/comptime.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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') {
Expand Down
15 changes: 0 additions & 15 deletions vlib/v/checker/tests/comptime_const/using_comptime_const.vv

This file was deleted.

13 changes: 13 additions & 0 deletions vlib/v/checker/tests/comptime_value/default_comptime_values_test.v
Original file line number Diff line number Diff line change
@@ -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`
}
3 changes: 3 additions & 0 deletions vlib/v/checker/tests/comptime_value/parser_errors_1.run.out
Original file line number Diff line number Diff line change
@@ -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))
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 change: 1 addition & 0 deletions vlib/v/checker/tests/comptime_value/parser_errors_1.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const my_f32 = $compile_value('my_f32', f32(42.0))
15 changes: 15 additions & 0 deletions vlib/v/checker/tests/comptime_value/use_flag_comptime_values.vv
Original file line number Diff line number Diff line change
@@ -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`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
42.0
false
done
11 changes: 11 additions & 0 deletions vlib/v/checker/tests/comptime_value/using_comptime_value.vv
Original file line number Diff line number Diff line change
@@ -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')
}
9 changes: 9 additions & 0 deletions vlib/v/fmt/fmt.v
Original file line number Diff line number Diff line change
Expand Up @@ -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})')
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/fmt/tests/comptime_value_keep.vv
Original file line number Diff line number Diff line change
@@ -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`)
}
3 changes: 2 additions & 1 deletion vlib/v/gen/c/comptime.v
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
if node.method_name == 'compile_value' {
// $compile_value('some_string',<default value>)
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}')
}
Expand Down

0 comments on commit a3ee35a

Please sign in to comment.