mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
parser,checker: add top level comptime support for $match @OS {
too (#25222)
Some checks failed
toml CI / toml-module-pass-external-test-suites (push) Has been cancelled
Tools CI / tools-linux (gcc) (push) Has been cancelled
Tools CI / tools-linux (tcc) (push) Has been cancelled
Tools CI / tools-macos (clang) (push) Has been cancelled
vab CI / vab-compiles-v-examples (push) Has been cancelled
vlib modules CI / build-module-docs (push) Has been cancelled
native backend CI / native-backend-ubuntu (push) Has been cancelled
native backend CI / native-backend-windows (push) Has been cancelled
Graphics CI / gg-regressions (push) Has been cancelled
Shy and PV CI / v-compiles-puzzle-vibes (push) Has been cancelled
Sanitized CI / sanitize-undefined-clang (push) Has been cancelled
Sanitized CI / sanitize-undefined-gcc (push) Has been cancelled
Sanitized CI / tests-sanitize-address-clang (push) Has been cancelled
Sanitized CI / sanitize-address-msvc (push) Has been cancelled
Sanitized CI / sanitize-address-gcc (push) Has been cancelled
Sanitized CI / sanitize-memory-clang (push) Has been cancelled
sdl CI / v-compiles-sdl-examples (push) Has been cancelled
Time CI / time-linux (push) Has been cancelled
Time CI / time-macos (push) Has been cancelled
Time CI / time-windows (push) Has been cancelled
Tools CI / tools-linux (clang) (push) Has been cancelled
Tools CI / tools-windows (gcc) (push) Has been cancelled
Tools CI / tools-windows (msvc) (push) Has been cancelled
Tools CI / tools-windows (tcc) (push) Has been cancelled
Tools CI / tools-docker-ubuntu-musl (push) Has been cancelled
vab CI / v-compiles-os-android (push) Has been cancelled
wasm backend CI / wasm-backend (ubuntu-22.04) (push) Has been cancelled
wasm backend CI / wasm-backend (windows-2022) (push) Has been cancelled
Some checks failed
toml CI / toml-module-pass-external-test-suites (push) Has been cancelled
Tools CI / tools-linux (gcc) (push) Has been cancelled
Tools CI / tools-linux (tcc) (push) Has been cancelled
Tools CI / tools-macos (clang) (push) Has been cancelled
vab CI / vab-compiles-v-examples (push) Has been cancelled
vlib modules CI / build-module-docs (push) Has been cancelled
native backend CI / native-backend-ubuntu (push) Has been cancelled
native backend CI / native-backend-windows (push) Has been cancelled
Graphics CI / gg-regressions (push) Has been cancelled
Shy and PV CI / v-compiles-puzzle-vibes (push) Has been cancelled
Sanitized CI / sanitize-undefined-clang (push) Has been cancelled
Sanitized CI / sanitize-undefined-gcc (push) Has been cancelled
Sanitized CI / tests-sanitize-address-clang (push) Has been cancelled
Sanitized CI / sanitize-address-msvc (push) Has been cancelled
Sanitized CI / sanitize-address-gcc (push) Has been cancelled
Sanitized CI / sanitize-memory-clang (push) Has been cancelled
sdl CI / v-compiles-sdl-examples (push) Has been cancelled
Time CI / time-linux (push) Has been cancelled
Time CI / time-macos (push) Has been cancelled
Time CI / time-windows (push) Has been cancelled
Tools CI / tools-linux (clang) (push) Has been cancelled
Tools CI / tools-windows (gcc) (push) Has been cancelled
Tools CI / tools-windows (msvc) (push) Has been cancelled
Tools CI / tools-windows (tcc) (push) Has been cancelled
Tools CI / tools-docker-ubuntu-musl (push) Has been cancelled
vab CI / v-compiles-os-android (push) Has been cancelled
wasm backend CI / wasm-backend (ubuntu-22.04) (push) Has been cancelled
wasm backend CI / wasm-backend (windows-2022) (push) Has been cancelled
This commit is contained in:
parent
2e8cc75e5f
commit
f6b60e4d9f
6 changed files with 218 additions and 11 deletions
|
@ -1018,6 +1018,30 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, mut sb strings.Builder) (
|
|||
}
|
||||
.eq, .ne, .gt, .lt, .ge, .le {
|
||||
match mut cond.left {
|
||||
ast.AtExpr {
|
||||
// @OS == 'linux'
|
||||
left_type := c.expr(mut cond.left)
|
||||
right_type := c.expr(mut cond.right)
|
||||
if !c.check_types(right_type, left_type) {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
c.error('mismatched types `${left_name}` and `${right_name}`',
|
||||
cond.pos)
|
||||
}
|
||||
left_str := cond.left.val
|
||||
right_str := (cond.right as ast.StringLiteral).val
|
||||
if cond.op == .eq {
|
||||
is_true = left_str == right_str
|
||||
} else if cond.op == .ne {
|
||||
is_true = left_str != right_str
|
||||
} else {
|
||||
c.error('string type only support `==` and `!=` operator',
|
||||
cond.pos)
|
||||
return false, false
|
||||
}
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, false
|
||||
}
|
||||
ast.Ident {
|
||||
// $if version == 2
|
||||
left_type := c.expr(mut cond.left)
|
||||
|
|
|
@ -20,11 +20,15 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
|
|||
}
|
||||
mut cond_type := ast.void_type
|
||||
if node.is_comptime {
|
||||
// for field.name and generic type `T`
|
||||
if node.cond is ast.SelectorExpr {
|
||||
c.expr(mut node.cond)
|
||||
if node.cond is ast.AtExpr {
|
||||
cond_type = c.expr(mut node.cond)
|
||||
} else {
|
||||
// for field.name and generic type `T`
|
||||
if node.cond is ast.SelectorExpr {
|
||||
c.expr(mut node.cond)
|
||||
}
|
||||
cond_type = c.get_expr_type(node.cond)
|
||||
}
|
||||
cond_type = c.get_expr_type(node.cond)
|
||||
} else {
|
||||
cond_type = c.expr(mut node.cond)
|
||||
}
|
||||
|
@ -69,7 +73,7 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
|
|||
c.expr(mut node.cond)
|
||||
if !c.type_resolver.is_generic_param_var(node.cond) {
|
||||
match mut node.cond {
|
||||
ast.StringLiteral {
|
||||
ast.StringLiteral, ast.AtExpr {
|
||||
comptime_match_cond_value = node.cond.val
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
|
|
|
@ -6,6 +6,7 @@ module parser
|
|||
import v.ast
|
||||
import v.token
|
||||
import v.pkgconfig
|
||||
import v.pref
|
||||
|
||||
fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
|
||||
was_inside_if_expr := p.inside_if_expr
|
||||
|
@ -193,7 +194,9 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
|
|||
return ast.IfExpr{}
|
||||
}
|
||||
p.open_scope()
|
||||
if is_comptime && comptime_skip_curr_stmts && !p.pref.is_fmt && !p.pref.output_cross_c {
|
||||
if is_comptime && comptime_skip_curr_stmts
|
||||
&& p.is_in_top_level_comptime(p.inside_assign_rhs) && !p.pref.is_fmt
|
||||
&& !p.pref.output_cross_c {
|
||||
p.skip_scope()
|
||||
branches << ast.IfBranch{
|
||||
cond: cond
|
||||
|
@ -278,29 +281,64 @@ fn (mut p Parser) is_match_sumtype_type() bool {
|
|||
&& next_next_tok.lit.len > 0 && next_next_tok.lit[0].is_capital()))
|
||||
}
|
||||
|
||||
fn (mut p Parser) resolve_at_expr(expr ast.AtExpr) !string {
|
||||
match expr.kind {
|
||||
.mod_name {
|
||||
return p.mod
|
||||
}
|
||||
.os {
|
||||
return pref.get_host_os().lower()
|
||||
}
|
||||
.ccompiler {
|
||||
return p.pref.ccompiler_type.str()
|
||||
}
|
||||
.backend {
|
||||
return p.pref.backend.str()
|
||||
}
|
||||
.platform {
|
||||
return p.pref.arch.str()
|
||||
}
|
||||
else {
|
||||
return error('top level comptime only support `@MOD` `@OS` `@CCOMPILER` `@BACKEND` or `@PLATFORM`')
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (mut p Parser) match_expr(is_comptime bool) ast.MatchExpr {
|
||||
mut match_first_pos := p.tok.pos()
|
||||
old_inside_ct_match := p.inside_ct_match
|
||||
if is_comptime {
|
||||
p.next() // `$`
|
||||
match_first_pos = p.prev_tok.pos().extend(p.tok.pos())
|
||||
p.inside_ct_match = true
|
||||
}
|
||||
old_inside_match := p.inside_match
|
||||
p.inside_match = true
|
||||
p.check(.key_match)
|
||||
mut is_sum_type := false
|
||||
cond := p.expr(0)
|
||||
mut cond_str := ''
|
||||
if is_comptime && cond is ast.AtExpr && p.is_in_top_level_comptime(p.inside_assign_rhs) {
|
||||
cond_str = p.resolve_at_expr(cond) or {
|
||||
p.error(err.msg())
|
||||
return ast.MatchExpr{}
|
||||
}
|
||||
}
|
||||
p.inside_match = old_inside_match
|
||||
p.inside_ct_match = old_inside_ct_match
|
||||
no_lcbr := p.tok.kind != .lcbr
|
||||
if !no_lcbr {
|
||||
p.check(.lcbr)
|
||||
}
|
||||
comments := p.eat_comments() // comments before the first branch
|
||||
mut branches := []ast.MatchBranch{}
|
||||
mut comptime_skip_curr_stmts := false
|
||||
mut comptime_has_true_branch := false
|
||||
for p.tok.kind != .eof {
|
||||
branch_first_pos := p.tok.pos()
|
||||
mut exprs := []ast.Expr{}
|
||||
mut ecmnts := [][]ast.Comment{}
|
||||
p.open_scope()
|
||||
// final else
|
||||
mut is_else := false
|
||||
if is_comptime {
|
||||
|
@ -352,19 +390,44 @@ fn (mut p Parser) match_expr(is_comptime bool) ast.MatchExpr {
|
|||
}
|
||||
// Expression match
|
||||
for {
|
||||
if is_comptime {
|
||||
p.inside_ct_match_case = true
|
||||
}
|
||||
p.inside_match_case = true
|
||||
mut range_pos := p.tok.pos()
|
||||
expr := p.expr(0)
|
||||
mut case_str := ''
|
||||
mut expr := p.expr(0)
|
||||
p.inside_match_case = false
|
||||
p.inside_ct_match_case = false
|
||||
match mut expr {
|
||||
ast.StringLiteral {
|
||||
case_str = expr.val
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
case_str = expr.val.str()
|
||||
}
|
||||
ast.BoolLiteral {
|
||||
case_str = expr.val.str()
|
||||
}
|
||||
else {}
|
||||
}
|
||||
comptime_skip_curr_stmts = cond_str != case_str
|
||||
if !comptime_skip_curr_stmts {
|
||||
comptime_has_true_branch = true
|
||||
}
|
||||
if p.tok.kind == .dotdot {
|
||||
p.error_with_pos('match only supports inclusive (`...`) ranges, not exclusive (`..`)',
|
||||
p.tok.pos())
|
||||
return ast.MatchExpr{}
|
||||
} else if p.tok.kind == .ellipsis {
|
||||
p.next()
|
||||
if is_comptime {
|
||||
p.inside_ct_match_case = true
|
||||
}
|
||||
p.inside_match_case = true
|
||||
expr2 := p.expr(0)
|
||||
p.inside_match_case = false
|
||||
p.inside_ct_match_case = false
|
||||
exprs << ast.RangeExpr{
|
||||
low: expr
|
||||
high: expr2
|
||||
|
@ -394,11 +457,24 @@ fn (mut p Parser) match_expr(is_comptime bool) ast.MatchExpr {
|
|||
}
|
||||
branch_last_pos := p.prev_tok.pos()
|
||||
// p.warn('match block')
|
||||
if is_comptime {
|
||||
p.inside_ct_match_body = true
|
||||
}
|
||||
p.inside_match_body = true
|
||||
stmts := p.parse_block_no_scope(false)
|
||||
p.open_scope()
|
||||
mut stmts := []ast.Stmt{}
|
||||
if is_comptime && ((!is_else && comptime_skip_curr_stmts)
|
||||
|| (is_else && comptime_has_true_branch))
|
||||
&& p.is_in_top_level_comptime(p.inside_assign_rhs) && !p.pref.is_fmt
|
||||
&& !p.pref.output_cross_c {
|
||||
p.skip_scope()
|
||||
} else {
|
||||
stmts = p.parse_block_no_scope(false)
|
||||
}
|
||||
branch_scope := p.scope
|
||||
p.close_scope()
|
||||
p.inside_match_body = false
|
||||
p.inside_ct_match_body = false
|
||||
pos := branch_first_pos.extend_with_last_line(branch_last_pos, p.prev_tok.line_nr)
|
||||
branch_pos := branch_first_pos.extend_with_last_line(p.tok.pos(), p.tok.line_nr)
|
||||
post_comments := p.eat_comments()
|
||||
|
@ -643,6 +719,27 @@ fn (mut p Parser) comptime_if_cond(mut cond ast.Expr) bool {
|
|||
}
|
||||
.eq, .ne, .gt, .lt, .ge, .le {
|
||||
match mut cond.left {
|
||||
ast.AtExpr {
|
||||
// @OS == 'linux'
|
||||
left_str := p.resolve_at_expr(cond.left) or {
|
||||
p.error(err.msg())
|
||||
return false
|
||||
}
|
||||
if cond.right !is ast.StringLiteral {
|
||||
p.error('`${cond.left} can only compare with string type')
|
||||
return false
|
||||
}
|
||||
right_str := (cond.right as ast.StringLiteral).val
|
||||
if cond.op == .eq {
|
||||
is_true = left_str == right_str
|
||||
} else if cond.op == .ne {
|
||||
is_true = left_str != right_str
|
||||
} else {
|
||||
p.error('string type only support `==` and `!=` operator')
|
||||
return false
|
||||
}
|
||||
return is_true
|
||||
}
|
||||
ast.Ident {
|
||||
// $if version == 2
|
||||
match mut cond.right {
|
||||
|
|
|
@ -53,6 +53,9 @@ mut:
|
|||
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
||||
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
||||
inside_match_body bool // to fix eval not used TODO
|
||||
inside_ct_match bool
|
||||
inside_ct_match_case bool
|
||||
inside_ct_match_body bool
|
||||
inside_unsafe bool
|
||||
inside_sum_type bool // to prevent parsing inline sum type again
|
||||
inside_asm_template bool
|
||||
|
@ -455,8 +458,9 @@ fn (mut p Parser) parse_block() []ast.Stmt {
|
|||
|
||||
fn (mut p Parser) is_in_top_level_comptime(inside_assign_rhs bool) bool {
|
||||
// TODO: find out a better way detect we are in top level.
|
||||
return p.cur_fn_name.len == 0 && p.inside_ct_if_expr && !inside_assign_rhs && !p.script_mode
|
||||
&& p.tok.kind != .name
|
||||
return p.cur_fn_name.len == 0
|
||||
&& (p.inside_ct_if_expr || p.inside_ct_match || p.inside_ct_match_body)
|
||||
&& !inside_assign_rhs && !p.script_mode && p.tok.kind != .name
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt {
|
||||
|
|
34
vlib/v/tests/comptime/comptime_if_at_expr_test.v
Normal file
34
vlib/v/tests/comptime/comptime_if_at_expr_test.v
Normal file
|
@ -0,0 +1,34 @@
|
|||
module main
|
||||
|
||||
$if @MOD == 'main' {
|
||||
const c1 = 'main'
|
||||
} $else {
|
||||
const c1 = 'other'
|
||||
}
|
||||
|
||||
$if @OS == 'linux' {
|
||||
const os = 'linux'
|
||||
} $else $if @OS == 'windows' {
|
||||
const os = 'windows'
|
||||
} $else {
|
||||
const os = 'other'
|
||||
}
|
||||
|
||||
fn test_comptime_if_at_expr() {
|
||||
assert c1 == 'main'
|
||||
|
||||
$if linux {
|
||||
assert os == 'linux'
|
||||
} $else $if windows {
|
||||
assert os == 'windows'
|
||||
} $else {
|
||||
assert os == 'other'
|
||||
}
|
||||
|
||||
dump(@FN)
|
||||
$if @FN == 'test_comptime_if_at_expr' {
|
||||
assert true
|
||||
} $else {
|
||||
assert false
|
||||
}
|
||||
}
|
44
vlib/v/tests/comptime/comptime_match_at_expr_test.v
Normal file
44
vlib/v/tests/comptime/comptime_match_at_expr_test.v
Normal file
|
@ -0,0 +1,44 @@
|
|||
module main
|
||||
|
||||
$match @MOD {
|
||||
'main' {
|
||||
const c1 = 'main'
|
||||
}
|
||||
$else {
|
||||
const c1 = 'other'
|
||||
}
|
||||
}
|
||||
|
||||
$match @OS {
|
||||
'linux' {
|
||||
const os = 'linux'
|
||||
}
|
||||
'windows' {
|
||||
const os = 'windows'
|
||||
}
|
||||
$else {
|
||||
const os = 'other'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_comptime_match_at_expr() {
|
||||
assert c1 == 'main'
|
||||
|
||||
dump(@FN)
|
||||
$match @FN {
|
||||
'test_comptime_match_at_expr' {
|
||||
assert true
|
||||
}
|
||||
$else {
|
||||
assert false
|
||||
}
|
||||
}
|
||||
|
||||
$if linux {
|
||||
assert os == 'linux'
|
||||
} $else $if windows {
|
||||
assert os == 'windows'
|
||||
} $else {
|
||||
assert os == 'other'
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue