parser,fmt,markused: add top level comptime $if support (enable $if platform { import module struct Abc {} }) (#25216)
Some checks are pending
Graphics CI / gg-regressions (push) Waiting to run
vlib modules CI / build-module-docs (push) Waiting to run
native backend CI / native-backend-ubuntu (push) Waiting to run
native backend CI / native-backend-windows (push) Waiting to run
Shy and PV CI / v-compiles-puzzle-vibes (push) Waiting to run
Sanitized CI / sanitize-undefined-clang (push) Waiting to run
Sanitized CI / sanitize-undefined-gcc (push) Waiting to run
Sanitized CI / tests-sanitize-address-clang (push) Waiting to run
Sanitized CI / sanitize-address-msvc (push) Waiting to run
Sanitized CI / sanitize-address-gcc (push) Waiting to run
Sanitized CI / sanitize-memory-clang (push) Waiting to run
sdl CI / v-compiles-sdl-examples (push) Waiting to run
Time CI / time-linux (push) Waiting to run
Time CI / time-macos (push) Waiting to run
Time CI / time-windows (push) Waiting to run
toml CI / toml-module-pass-external-test-suites (push) Waiting to run
Tools CI / tools-linux (clang) (push) Waiting to run
Tools CI / tools-linux (gcc) (push) Waiting to run
Tools CI / tools-linux (tcc) (push) Waiting to run
Tools CI / tools-macos (clang) (push) Waiting to run
Tools CI / tools-windows (gcc) (push) Waiting to run
Tools CI / tools-windows (msvc) (push) Waiting to run
Tools CI / tools-windows (tcc) (push) Waiting to run
Tools CI / tools-docker-ubuntu-musl (push) Waiting to run
vab CI / v-compiles-os-android (push) Waiting to run
vab CI / vab-compiles-v-examples (push) Waiting to run
wasm backend CI / wasm-backend (ubuntu-22.04) (push) Waiting to run
wasm backend CI / wasm-backend (windows-2022) (push) Waiting to run

This commit is contained in:
kbkpbot 2025-09-02 18:01:37 +08:00 committed by GitHub
parent 344f7ccdcb
commit 19d31f221f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 955 additions and 434 deletions

View file

@ -1,5 +1,7 @@
module ast module ast
import v.pref
pub const valid_comptime_if_os = ['windows', 'ios', 'macos', 'mach', 'darwin', 'hpux', 'gnu', 'qnx', pub const valid_comptime_if_os = ['windows', 'ios', 'macos', 'mach', 'darwin', 'hpux', 'gnu', 'qnx',
'linux', 'freebsd', 'openbsd', 'netbsd', 'bsd', 'dragonfly', 'android', 'termux', 'solaris', 'linux', 'freebsd', 'openbsd', 'netbsd', 'bsd', 'dragonfly', 'android', 'termux', 'solaris',
'haiku', 'serenity', 'vinix', 'plan9', 'wasm32_emscripten'] 'haiku', 'serenity', 'vinix', 'plan9', 'wasm32_emscripten']
@ -22,3 +24,225 @@ fn all_valid_comptime_idents() []string {
res << valid_comptime_if_other res << valid_comptime_if_other
return res return res
} }
pub fn eval_comptime_not_user_defined_ident(ident string, the_pref &pref.Preferences) !bool {
mut is_true := false
if ident in valid_comptime_if_os {
if ident_enum_val := pref.os_from_string(ident) {
if ident_enum_val == the_pref.os {
is_true = true
}
}
} else if ident in valid_comptime_if_compilers {
is_true = pref.cc_from_string(ident) == the_pref.ccompiler_type
} else if ident in valid_comptime_if_platforms {
match ident {
'amd64' {
is_true = the_pref.arch == .amd64
}
'i386' {
is_true = the_pref.arch == .i386
}
'aarch64' {
is_true = the_pref.arch == .arm64
}
'arm64' {
is_true = the_pref.arch == .arm64
}
'arm32' {
is_true = the_pref.arch == .arm32
}
'rv64' {
is_true = the_pref.arch == .rv64
}
'rv32' {
is_true = the_pref.arch == .rv32
}
's390x' {
is_true = the_pref.arch == .s390x
}
'ppc64le' {
is_true = the_pref.arch == .ppc64le
}
'loongarch64' {
is_true = the_pref.arch == .loongarch64
}
else {
return error('invalid \$if condition: unknown platforms `${ident}`')
}
}
} else if ident in valid_comptime_if_cpu_features {
match ident {
'x64' {
is_true = the_pref.m64
}
'x32' {
is_true = !the_pref.m64
}
'little_endian' {
is_true = $if little_endian { true } $else { false }
}
'big_endian' {
is_true = $if big_endian { true } $else { false }
}
else {
return error('invalid \$if condition: unknown cpu_features `${ident}`')
}
}
} else if ident in valid_comptime_if_other {
match ident {
'apk' {
is_true = the_pref.is_apk
}
'js' {
is_true = the_pref.backend.is_js()
}
'debug' {
is_true = the_pref.is_debug
}
'prod' {
is_true = the_pref.is_prod
}
'test' {
is_true = the_pref.is_test
}
'glibc' {
is_true = the_pref.is_glibc
}
'prealloc' {
is_true = the_pref.prealloc
}
'no_bounds_checking' {
is_true = the_pref.no_bounds_checking
}
'freestanding' {
is_true = the_pref.is_bare && !the_pref.output_cross_c
}
'threads' {
return error('threads should handle outside of `check_valid_ident()`')
}
'js_node' {
is_true = the_pref.backend == .js_node
}
'js_browser' {
is_true = the_pref.backend == .js_browser
}
'js_freestanding' {
is_true = the_pref.backend == .js_freestanding
}
'interpreter' {
is_true = the_pref.backend == .interpret
}
'es5' {
is_true = the_pref.output_es5
}
'profile' {
is_true = the_pref.is_prof
}
'wasm32' {
is_true = the_pref.arch == .wasm32
}
'wasm32_wasi' {
is_true = the_pref.os == .wasm32_wasi
}
'fast_math' {
is_true = the_pref.fast_math
}
'native' {
is_true = the_pref.backend == .native
}
'autofree' {
is_true = the_pref.autofree
}
else {
return error('invalid \$if condition: unknown other indent `${ident}`')
}
}
} else if ident in the_pref.compile_defines {
is_true = true
} else {
return error('invalid \$if condition: unknown indent `${ident}`')
}
return is_true
}
pub const system_ident_map = {
// OS
'windows': '_WIN32'
'ios': '__TARGET_IOS__'
'macos': '__APPLE__'
'mach': '__MACH__'
'darwin': '__DARWIN__'
'hpux': '__HPUX__'
'gnu': '__GNU__'
'qnx': '__QNX__'
'linux': '__linux__'
'serenity': '__serenity__'
'plan9': '__plan9__'
'vinix': '__vinix__'
'freebsd': '__FreeBSD__'
'openbsd': '__OpenBSD__'
'netbsd': '__NetBSD__'
'bsd': '__BSD__'
'dragonfly': '__DragonFly__'
'android': '__ANDROID__'
'termux': '__TERMUX__'
'solaris': '__sun'
'haiku': '__HAIKU__'
// Backend
'js': '_VJS'
'wasm32_emscripten': '__EMSCRIPTEN__'
'native': '_VNATIVE'
// Compiler
'gcc': '__V_GCC__'
'tinyc': '__TINYC__'
'clang': '__clang__'
'mingw': '__MINGW32__'
'msvc': '_MSC_VER'
'cplusplus': '__cplusplus'
// Others
'threads': '__VTHREADS__'
'gcboehm': '_VGCBOEHM'
'debug': '_VDEBUG'
'prod': '_VPROD'
'profile': '_VPROFILE'
'test': '_VTEST'
'glibc': '__GLIBC__'
'prealloc': '_VPREALLOC'
'no_bounds_checking': 'CUSTOM_DEFINE_no_bounds_checking'
'freestanding': '_VFREESTANDING'
'autofree': '_VAUTOFREE'
// CPU
'amd64': '__V_amd64'
'aarch64': '__V_arm64'
'arm64': '__V_arm64' // aarch64 alias
'arm32': '__V_arm32'
'i386': '__V_x86'
'rv64': '__V_rv64'
'riscv64': '__V_rv64' // rv64 alias
'rv32': '__V_rv32'
'riscv32': '__V_rv32' // rv32 alias
's390x': '__V_s390x'
'ppc64le': '__V_ppc64le'
'loongarch64': '__V_loongarch64'
'x64': 'TARGET_IS_64BIT'
'x32': 'TARGET_IS_32BIT'
'little_endian': 'TARGET_ORDER_IS_LITTLE'
'big_endian': 'TARGET_ORDER_IS_BIG'
}
pub fn comptime_if_to_ifdef(name string, the_pref &pref.Preferences) !string {
if name == 'fast_math' {
return if the_pref.ccompiler_type == .msvc {
// turned on by: `-cflags /fp:fast`
'_M_FP_FAST'
} else {
// turned on by: `-cflags -ffast-math`
'__FAST_MATH__'
}
}
if ifdef := system_ident_map[name] {
return ifdef
}
return error('bad os ifdef name `${name}`')
}

View file

@ -784,191 +784,6 @@ fn (mut c Checker) evaluate_once_comptime_if_attribute(mut node ast.Attr) bool {
return node.ct_skip return node.ct_skip
} }
fn (mut c Checker) comptime_if_to_ifdef(name string) !string {
match name {
// platforms/os-es:
'windows' {
return '_WIN32'
}
'ios' {
return '__TARGET_IOS__'
}
'macos' {
return '__APPLE__'
}
'mach' {
return '__MACH__'
}
'darwin' {
return '__DARWIN__'
}
'hpux' {
return '__HPUX__'
}
'gnu' {
return '__GNU__'
}
'qnx' {
return '__QNX__'
}
'linux' {
return '__linux__'
}
'serenity' {
return '__serenity__'
}
'plan9' {
return '__plan9__'
}
'vinix' {
return '__vinix__'
}
'freebsd' {
return '__FreeBSD__'
}
'openbsd' {
return '__OpenBSD__'
}
'netbsd' {
return '__NetBSD__'
}
'bsd' {
return '__BSD__'
}
'dragonfly' {
return '__DragonFly__'
}
'android' {
return '__ANDROID__'
}
'termux' {
// Note: termux is running on Android natively so __ANDROID__ will also be defined
return '__TERMUX__'
}
'solaris' {
return '__sun'
}
'haiku' {
return '__HAIKU__'
}
//
'js' {
return '_VJS'
}
'wasm32_emscripten' {
return '__EMSCRIPTEN__'
}
'native' {
return '_VNATIVE' // when using the native backend, cgen is inactive
}
// compilers:
'gcc' {
return '__V_GCC__'
}
'tinyc' {
return '__TINYC__'
}
'clang' {
return '__clang__'
}
'mingw' {
return '__MINGW32__'
}
'msvc' {
return '_MSC_VER'
}
'cplusplus' {
return '__cplusplus'
}
// other:
'threads' {
return '__VTHREADS__'
}
'gcboehm' {
return '_VGCBOEHM'
}
'debug' {
return '_VDEBUG'
}
'prod' {
return '_VPROD'
}
'profile' {
return '_VPROFILE'
}
'test' {
return '_VTEST'
}
'glibc' {
return '__GLIBC__'
}
'prealloc' {
return '_VPREALLOC'
}
'no_bounds_checking' {
return 'CUSTOM_DEFINE_no_bounds_checking'
}
'freestanding' {
return '_VFREESTANDING'
}
'autofree' {
return '_VAUTOFREE'
}
// architectures:
'amd64' {
return '__V_amd64'
}
'aarch64', 'arm64' {
return '__V_arm64'
}
'arm32' {
return '__V_arm32'
}
'i386' {
return '__V_x86'
}
'rv64', 'riscv64' {
return '__V_rv64'
}
'rv32', 'riscv32' {
return '__V_rv32'
}
's390x' {
return '__V_s390x'
}
'ppc64le' {
return '__V_ppc64le'
}
'loongarch64' {
return '__V_loongarch64'
}
// bitness:
'x64' {
return 'TARGET_IS_64BIT'
}
'x32' {
return 'TARGET_IS_32BIT'
}
// endianness:
'little_endian' {
return 'TARGET_ORDER_IS_LITTLE'
}
'big_endian' {
return 'TARGET_ORDER_IS_BIG'
}
'fast_math' {
if c.pref.ccompiler_type == .msvc {
// turned on by: `-cflags /fp:fast`
return '_M_FP_FAST'
}
// turned on by: `-cflags -ffast-math`
return '__FAST_MATH__'
}
else {}
}
return error('bad os ifdef name "${name}"')
}
// check if `ident` is a function generic, such as `T` // check if `ident` is a function generic, such as `T`
fn (mut c Checker) is_generic_ident(ident string) bool { fn (mut c Checker) is_generic_ident(ident string) bool {
if !isnil(c.table.cur_fn) && ident in c.table.cur_fn.generic_names if !isnil(c.table.cur_fn) && ident in c.table.cur_fn.generic_names
@ -1141,10 +956,6 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, mut sb strings.Builder) (
should_record_ident = true should_record_ident = true
is_user_ident = true is_user_ident = true
ident_name = cname ident_name = cname
// ifdef := c.comptime_if_to_ifdef(cname, true) or {
// c.error(err.msg(), cond.pos)
// return false, false
//}
sb.write_string('defined(CUSTOM_DEFINE_${cname})') sb.write_string('defined(CUSTOM_DEFINE_${cname})')
is_true = cname in c.pref.compile_defines is_true = cname in c.pref.compile_defines
return is_true, false return is_true, false
@ -1483,143 +1294,12 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, mut sb strings.Builder) (
should_record_ident = true should_record_ident = true
is_user_ident = false is_user_ident = false
ident_name = cname ident_name = cname
if cname in ast.valid_comptime_if_os { if cname in ast.valid_comptime_not_user_defined {
if cname_enum_val := pref.os_from_string(cname) { if cname == 'threads' {
if cname_enum_val == c.pref.os {
is_true = true
}
}
} else if cname in ast.valid_comptime_if_compilers {
is_true = pref.cc_from_string(cname) == c.pref.ccompiler_type
} else if cname in ast.valid_comptime_if_platforms {
if cname == 'aarch64' {
c.note('use `arm64` instead of `aarch64`', cond.pos)
}
match cname {
'amd64' {
is_true = c.pref.arch == .amd64
}
'i386' {
is_true = c.pref.arch == .i386
}
'aarch64' {
is_true = c.pref.arch == .arm64
}
'arm64' {
is_true = c.pref.arch == .arm64
}
'arm32' {
is_true = c.pref.arch == .arm32
}
'rv64' {
is_true = c.pref.arch == .rv64
}
'rv32' {
is_true = c.pref.arch == .rv32
}
's390x' {
is_true = c.pref.arch == .s390x
}
'ppc64le' {
is_true = c.pref.arch == .ppc64le
}
'loongarch64' {
is_true = c.pref.arch == .loongarch64
}
else {
c.error('invalid \$if condition: unknown platforms `${cname}`',
cond.pos)
return false, false
}
}
} else if cname in ast.valid_comptime_if_cpu_features {
match cname {
'x64' {
is_true = c.pref.m64
}
'x32' {
is_true = !c.pref.m64
}
'little_endian' {
is_true = $if little_endian { true } $else { false }
}
'big_endian' {
is_true = $if big_endian { true } $else { false }
}
else {
c.error('invalid \$if condition: unknown cpu_features `${cname}`',
cond.pos)
return false, false
}
}
} else if cname in ast.valid_comptime_if_other {
match cname {
'apk' {
is_true = c.pref.is_apk
}
'js' {
is_true = c.pref.backend.is_js()
}
'debug' {
is_true = c.pref.is_debug
}
'prod' {
is_true = c.pref.is_prod
}
'test' {
is_true = c.pref.is_test
}
'glibc' {
is_true = c.pref.is_glibc
}
'prealloc' {
is_true = c.pref.prealloc
}
'no_bounds_checking' {
is_true = c.pref.no_bounds_checking
}
'freestanding' {
is_true = c.pref.is_bare && !c.pref.output_cross_c
}
'threads' {
is_true = c.table.gostmts > 0 is_true = c.table.gostmts > 0
} } else {
'js_node' { is_true = ast.eval_comptime_not_user_defined_ident(cname, c.pref) or {
is_true = c.pref.backend == .js_node c.error(err.msg(), cond.pos)
}
'js_browser' {
is_true = c.pref.backend == .js_browser
}
'js_freestanding' {
is_true = c.pref.backend == .js_freestanding
}
'interpreter' {
is_true = c.pref.backend == .interpret
}
'es5' {
is_true = c.pref.output_es5
}
'profile' {
is_true = c.pref.is_prof
}
'wasm32' {
is_true = c.pref.arch == .wasm32
}
'wasm32_wasi' {
is_true = c.pref.os == .wasm32_wasi
}
'fast_math' {
is_true = c.pref.fast_math
}
'native' {
is_true = c.pref.backend == .native
}
'autofree' {
is_true = c.pref.autofree
}
else {
c.error('invalid \$if condition: unknown other indent `${cname}`',
cond.pos)
return false, false return false, false
} }
} }
@ -1655,7 +1335,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, mut sb strings.Builder) (
c.error('invalid \$if condition: unknown indent `${cname}`', cond.pos) c.error('invalid \$if condition: unknown indent `${cname}`', cond.pos)
return false, false return false, false
} }
if ifdef := c.comptime_if_to_ifdef(cname) { if ifdef := ast.comptime_if_to_ifdef(cname, c.pref) {
sb.write_string('defined(${ifdef})') sb.write_string('defined(${ifdef})')
} else { } else {
sb.write_string('${is_true}') sb.write_string('${is_true}')

View file

@ -51,6 +51,8 @@ pub mut:
wsinfix_depth int wsinfix_depth int
format_state FormatState format_state FormatState
source_text string // can be set by `echo "println('hi')" | v fmt`, i.e. when processing source not from a file, but from stdin. In this case, it will contain the entire input text. You can use f.file.path otherwise, and read from that file. source_text string // can be set by `echo "println('hi')" | v fmt`, i.e. when processing source not from a file, but from stdin. In this case, it will contain the entire input text. You can use f.file.path otherwise, and read from that file.
global_processed_imports []string
branch_processed_imports []string
} }
@[params] @[params]
@ -317,12 +319,17 @@ pub fn (mut f Fmt) import_stmt(imp ast.Import) {
return return
} }
imp_stmt := f.imp_stmt_str(imp) imp_stmt := f.imp_stmt_str(imp)
if imp_stmt in f.processed_imports { if imp_stmt in f.global_processed_imports
|| (f.inside_comptime_if && imp_stmt in f.branch_processed_imports) {
// Skip duplicates. // Skip duplicates.
f.import_comments(imp.next_comments) f.import_comments(imp.next_comments)
return return
} }
f.processed_imports << imp_stmt if f.inside_comptime_if {
f.branch_processed_imports << imp_stmt
} else {
f.global_processed_imports << imp_stmt
}
if !f.format_state.is_vfmt_on { if !f.format_state.is_vfmt_on {
original_imp_line := f.get_source_lines()#[imp.pos.line_nr..imp.pos.last_line + 1].join('\n') original_imp_line := f.get_source_lines()#[imp.pos.line_nr..imp.pos.last_line + 1].join('\n')
// Same line comments(`imp.comments`) are included in the `original_imp_line`. // Same line comments(`imp.comments`) are included in the `original_imp_line`.
@ -1070,7 +1077,7 @@ pub fn (mut f Fmt) enum_decl(node ast.EnumDecl) {
f.writeln('') f.writeln('')
f.comments(field.next_comments, has_nl: true, level: .indent) f.comments(field.next_comments, has_nl: true, level: .indent)
} }
f.writeln('}\n') f.writeln('}')
} }
pub fn (mut f Fmt) fn_decl(node ast.FnDecl) { pub fn (mut f Fmt) fn_decl(node ast.FnDecl) {
@ -2354,6 +2361,7 @@ pub fn (mut f Fmt) if_expr(node ast.IfExpr) {
start_len := f.line_len start_len := f.line_len
for { for {
for i, branch in node.branches { for i, branch in node.branches {
f.branch_processed_imports.clear()
mut sum_len := 0 mut sum_len := 0
if i > 0 { if i > 0 {
// `else`, close previous branch // `else`, close previous branch

View file

@ -0,0 +1,16 @@
$if new_c_1 ? {
pub enum Enum1 {
enum1_a
enum1_b
}
} $else $if new_c_2 ? {
pub enum Enum1 {
enum1_c
enum1_d
}
} $else {
pub enum Enum1 {
enum1_e
enum1_f
}
}

View file

@ -2,7 +2,6 @@ pub enum PubEnum {
foo foo
bar bar
} }
enum PrivateEnum { enum PrivateEnum {
foo foo
bar bar

View file

@ -331,18 +331,8 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
} }
} }
fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast.ConstField, map[string]ast.GlobalField, map[string]ast.TypeDecl, map[string]ast.StructDecl) { fn all_global_decl_in_stmts(stmts []ast.Stmt, mut all_fns map[string]ast.FnDecl, mut all_consts map[string]ast.ConstField, mut all_globals map[string]ast.GlobalField, mut all_decltypes map[string]ast.TypeDecl, mut all_structs map[string]ast.StructDecl) {
util.timing_start(@METHOD) for node in stmts {
defer {
util.timing_measure(@METHOD)
}
mut all_fns := map[string]ast.FnDecl{}
mut all_consts := map[string]ast.ConstField{}
mut all_globals := map[string]ast.GlobalField{}
mut all_decltypes := map[string]ast.TypeDecl{}
mut all_structs := map[string]ast.StructDecl{}
for i in 0 .. ast_files.len {
for node in ast_files[i].stmts {
match node { match node {
ast.FnDecl { ast.FnDecl {
fkey := node.fkey() fkey := node.fkey()
@ -370,9 +360,49 @@ fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast
all_decltypes[node.name] = node all_decltypes[node.name] = node
} }
} }
ast.ExprStmt {
match node.expr {
ast.IfExpr {
if node.expr.is_comptime {
// top level comptime $if
for branch in node.expr.branches {
all_global_decl_in_stmts(branch.stmts, mut all_fns, mut
all_consts, mut all_globals, mut all_decltypes, mut
all_structs)
}
}
}
ast.MatchExpr {
if node.expr.is_comptime {
// top level comptime $match
for branch in node.expr.branches {
all_global_decl_in_stmts(branch.stmts, mut all_fns, mut
all_consts, mut all_globals, mut all_decltypes, mut
all_structs)
}
}
}
else {} else {}
} }
} }
else {}
}
}
}
fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast.ConstField, map[string]ast.GlobalField, map[string]ast.TypeDecl, map[string]ast.StructDecl) {
util.timing_start(@METHOD)
defer {
util.timing_measure(@METHOD)
}
mut all_fns := map[string]ast.FnDecl{}
mut all_consts := map[string]ast.ConstField{}
mut all_globals := map[string]ast.GlobalField{}
mut all_decltypes := map[string]ast.TypeDecl{}
mut all_structs := map[string]ast.StructDecl{}
for i in 0 .. ast_files.len {
all_global_decl_in_stmts(ast_files[i].stmts, mut all_fns, mut all_consts, mut
all_globals, mut all_decltypes, mut all_structs)
} }
return all_fns, all_consts, all_globals, all_decltypes, all_structs return all_fns, all_consts, all_globals, all_decltypes, all_structs
} }

View file

@ -96,7 +96,10 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
// p.warn('enum val $val') // p.warn('enum val $val')
if p.tok.kind == .assign { if p.tok.kind == .assign {
p.next() p.next()
old_assign_rhs := p.inside_assign_rhs
p.inside_assign_rhs = true
expr = p.expr(0) expr = p.expr(0)
p.inside_assign_rhs = old_assign_rhs
has_expr = true has_expr = true
uses_exprs = true uses_exprs = true
} }
@ -225,7 +228,8 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
is_pub: is_pub is_pub: is_pub
}) })
if idx in [ast.string_type_idx, ast.rune_type_idx, ast.array_type_idx, ast.map_type_idx] { if idx in [ast.string_type_idx, ast.rune_type_idx, ast.array_type_idx, ast.map_type_idx]
&& !p.pref.is_fmt {
p.error_with_pos('cannot register enum `${name}`, another type with this name exists', p.error_with_pos('cannot register enum `${name}`, another type with this name exists',
end_pos) end_pos)
} }

View file

@ -359,6 +359,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
check_name = if language == .js { p.check_js_name() } else { p.check_name() } check_name = if language == .js { p.check_js_name() } else { p.check_name() }
name = check_name name = check_name
} }
if language == .v && !p.pref.translated && !p.is_translated && !p.builtin_mod if language == .v && !p.pref.translated && !p.is_translated && !p.builtin_mod
&& util.contains_capital(check_name) { && util.contains_capital(check_name) {
p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead', p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead',
@ -655,6 +656,7 @@ run them via `v file.v` instead',
}) })
*/ */
// Body // Body
keep_fn_name := p.cur_fn_name
p.cur_fn_name = name p.cur_fn_name = name
mut stmts := []ast.Stmt{} mut stmts := []ast.Stmt{}
body_start_pos := p.tok.pos() body_start_pos := p.tok.pos()
@ -671,6 +673,7 @@ run them via `v file.v` instead',
p.inside_unsafe_fn = false p.inside_unsafe_fn = false
p.inside_fn = false p.inside_fn = false
} }
p.cur_fn_name = keep_fn_name
if !no_body && are_params_type_only { if !no_body && are_params_type_only {
p.error_with_pos('functions with type only params can not have bodies', body_start_pos) p.error_with_pos('functions with type only params can not have bodies', body_start_pos)
return ast.FnDecl{ return ast.FnDecl{

View file

@ -5,6 +5,7 @@ module parser
import v.ast import v.ast
import v.token import v.token
import v.pkgconfig
fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr { fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
was_inside_if_expr := p.inside_if_expr was_inside_if_expr := p.inside_if_expr
@ -25,6 +26,8 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
mut has_else := false mut has_else := false
mut comments := []ast.Comment{} mut comments := []ast.Comment{}
mut prev_guard := false mut prev_guard := false
mut comptime_skip_curr_stmts := false
mut comptime_has_true_branch := false
for p.tok.kind in [.key_if, .key_else] { for p.tok.kind in [.key_if, .key_else] {
p.inside_if = true p.inside_if = true
if is_comptime { if is_comptime {
@ -58,6 +61,16 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
is_special: true is_special: true
}) })
} }
if is_comptime && comptime_has_true_branch && !p.pref.is_fmt
&& !p.pref.output_cross_c {
p.skip_scope()
branches << ast.IfBranch{
pos: start_pos.extend(end_pos)
body_pos: body_pos.extend(p.tok.pos())
comments: comments
scope: p.scope
}
} else {
branches << ast.IfBranch{ branches << ast.IfBranch{
stmts: p.parse_block_no_scope(false) stmts: p.parse_block_no_scope(false)
pos: start_pos.extend(end_pos) pos: start_pos.extend(end_pos)
@ -65,6 +78,7 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
comments: comments comments: comments
scope: p.scope scope: p.scope
} }
}
p.close_scope() p.close_scope()
comments = [] comments = []
break break
@ -114,7 +128,10 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
comments << p.eat_comments() comments << p.eat_comments()
p.check(.decl_assign) p.check(.decl_assign)
comments << p.eat_comments() comments << p.eat_comments()
old_assign_rhs := p.inside_assign_rhs
p.inside_assign_rhs = true
expr := p.expr(0) expr := p.expr(0)
p.inside_assign_rhs = old_assign_rhs
if expr !in [ast.CallExpr, ast.IndexExpr, ast.PrefixExpr, ast.SelectorExpr, ast.Ident] { if expr !in [ast.CallExpr, ast.IndexExpr, ast.PrefixExpr, ast.SelectorExpr, ast.Ident] {
p.error_with_pos('if guard condition expression is illegal, it should return an Option', p.error_with_pos('if guard condition expression is illegal, it should return an Option',
expr.pos()) expr.pos())
@ -141,6 +158,12 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
p.comptime_if_cond = true p.comptime_if_cond = true
p.inside_if_cond = true p.inside_if_cond = true
cond = p.expr(0) cond = p.expr(0)
if is_comptime && p.is_in_top_level_comptime(p.inside_assign_rhs) {
comptime_skip_curr_stmts = !p.comptime_if_cond(mut cond)
if !comptime_skip_curr_stmts {
comptime_has_true_branch = true
}
}
if mut cond is ast.InfixExpr && !is_comptime { if mut cond is ast.InfixExpr && !is_comptime {
if cond.op in [.key_is, .not_is] { if cond.op in [.key_is, .not_is] {
if mut cond.left is ast.Ident { if mut cond.left is ast.Ident {
@ -170,6 +193,16 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
return ast.IfExpr{} return ast.IfExpr{}
} }
p.open_scope() p.open_scope()
if is_comptime && comptime_skip_curr_stmts && !p.pref.is_fmt && !p.pref.output_cross_c {
p.skip_scope()
branches << ast.IfBranch{
cond: cond
pos: start_pos.extend(end_pos)
body_pos: body_pos.extend(p.prev_tok.pos())
comments: comments
scope: p.scope
}
} else {
stmts := p.parse_block_no_scope(false) stmts := p.parse_block_no_scope(false)
branches << ast.IfBranch{ branches << ast.IfBranch{
cond: cond cond: cond
@ -179,6 +212,7 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
comments: comments comments: comments
scope: p.scope scope: p.scope
} }
}
p.close_scope() p.close_scope()
if is_guard { if is_guard {
p.close_scope() p.close_scope()
@ -570,3 +604,167 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
has_exception: has_else || has_timeout has_exception: has_else || has_timeout
} }
} }
fn (mut p Parser) comptime_if_cond(mut cond ast.Expr) bool {
mut is_true := false
match mut cond {
ast.BoolLiteral {
return cond.val
}
ast.ParExpr {
return p.comptime_if_cond(mut cond.expr)
}
ast.PrefixExpr {
if cond.op != .not {
p.error('invalid \$if prefix operator, only allow `!`.')
return false
}
return !p.comptime_if_cond(mut cond.right)
}
ast.PostfixExpr {
if cond.op != .question {
p.error('invalid \$if postfix operator, only allow `?`.')
return false
}
if cond.expr !is ast.Ident {
p.error('invalid \$if postfix condition, only allow `Indent`.')
return false
}
cname := (cond.expr as ast.Ident).name
return cname in p.pref.compile_defines
}
ast.InfixExpr {
match cond.op {
.and, .logical_or {
l := p.comptime_if_cond(mut cond.left)
r := p.comptime_if_cond(mut cond.right)
// if at least one of the cond has `keep_stmts`, we should keep stmts
return if cond.op == .and { l && r } else { l || r }
}
.eq, .ne, .gt, .lt, .ge, .le {
match mut cond.left {
ast.Ident {
// $if version == 2
match mut cond.right {
ast.StringLiteral {
match cond.op {
.eq {
is_true = cond.left.str() == cond.right.str()
}
.ne {
is_true = cond.left.str() != cond.right.str()
}
else {
p.error('string type only support `==` and `!=` operator')
return false
}
}
}
ast.IntegerLiteral {
match cond.op {
.eq {
is_true = cond.left.str().i64() == cond.right.val.i64()
}
.ne {
is_true = cond.left.str().i64() != cond.right.val.i64()
}
.gt {
is_true = cond.left.str().i64() > cond.right.val.i64()
}
.lt {
is_true = cond.left.str().i64() < cond.right.val.i64()
}
.ge {
is_true = cond.left.str().i64() >= cond.right.val.i64()
}
.le {
is_true = cond.left.str().i64() <= cond.right.val.i64()
}
else {
p.error('int type only support `==` `!=` `>` `<` `>=` and `<=` operator')
return false
}
}
}
ast.BoolLiteral {
match cond.op {
.eq {
is_true = cond.left.str().bool() == cond.right.val
}
.ne {
is_true = cond.left.str().bool() != cond.right.val
}
else {
p.error('bool type only support `==` and `!=` operator')
return false
}
}
}
else {
p.error('compare only support string int and bool type')
return false
}
}
return is_true
}
else {
p.error('invalid \$if condition')
return false
}
}
p.error('invalid \$if condition')
return false
}
else {
p.error('invalid \$if operator: ${cond.op}')
return false
}
}
}
ast.Ident {
cname := cond.name
if cname in ast.valid_comptime_not_user_defined {
if cname == 'threads' {
is_true = p.table.gostmts > 0
} else {
is_true = ast.eval_comptime_not_user_defined_ident(cname, p.pref) or {
p.error(err.msg())
return false
}
}
} else {
p.error('invalid \$if condition: unknown indent `${cname}`')
return false
}
return is_true
}
ast.ComptimeCall {
if cond.kind == .pkgconfig {
if mut m := pkgconfig.main([cond.args_var]) {
if _ := m.run() {
is_true = true
} else {
// pkgconfig not found, do not issue error, just set false
is_true = false
}
} else {
p.error(err.msg())
is_true = false
}
return is_true
}
if cond.kind == .d {
is_true = cond.compile_value.bool()
return is_true
}
p.error('invalid \$if condition: unknown ComptimeCall')
return false
}
else {
p.error('invalid \$if condition ${cond}')
return false
}
}
return is_true
}

View file

@ -111,6 +111,7 @@ mut:
generic_type_level int // to avoid infinite recursion segfaults due to compiler bugs in ensure_type_exists generic_type_level int // to avoid infinite recursion segfaults due to compiler bugs in ensure_type_exists
main_already_defined bool // TODO move to checker main_already_defined bool // TODO move to checker
is_vls bool is_vls bool
inside_import_section bool
pub mut: pub mut:
scanner &scanner.Scanner = unsafe { nil } scanner &scanner.Scanner = unsafe { nil }
table &ast.Table = unsafe { nil } table &ast.Table = unsafe { nil }
@ -290,6 +291,7 @@ pub fn (mut p Parser) parse() &ast.File {
} else { } else {
stmts << module_decl stmts << module_decl
} }
p.inside_import_section = true
// imports // imports
for { for {
if p.tok.kind == .key_import { if p.tok.kind == .key_import {
@ -451,6 +453,12 @@ fn (mut p Parser) parse_block() []ast.Stmt {
return stmts return stmts
} }
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
}
fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt { fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt {
p.check(.lcbr) p.check(.lcbr)
mut stmts := []ast.Stmt{cap: 20} mut stmts := []ast.Stmt{cap: 20}
@ -459,7 +467,12 @@ fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt {
if p.tok.kind != .rcbr { if p.tok.kind != .rcbr {
mut count := 0 mut count := 0
for p.tok.kind !in [.eof, .rcbr] { for p.tok.kind !in [.eof, .rcbr] {
if p.is_in_top_level_comptime(old_assign_rhs) {
// top level `$if cond { println() }` should goto `p.stmt()`
stmts << p.top_stmt()
} else {
stmts << p.stmt(is_top_level) stmts << p.stmt(is_top_level)
}
count++ count++
if count % 100000 == 0 { if count % 100000 == 0 {
if p.is_vls { if p.is_vls {
@ -617,6 +630,10 @@ fn (p &Parser) trace_parser(label string) {
fn (mut p Parser) top_stmt() ast.Stmt { fn (mut p Parser) top_stmt() ast.Stmt {
p.trace_parser('top_stmt') p.trace_parser('top_stmt')
for { for {
if p.tok.kind !in [.key_import, .comment, .dollar] {
// import section should only prepend by `import`, `comment` or `$if`.
p.inside_import_section = false
}
match p.tok.kind { match p.tok.kind {
.key_pub { .key_pub {
match p.peek_tok.kind { match p.peek_tok.kind {
@ -660,8 +677,10 @@ fn (mut p Parser) top_stmt() ast.Stmt {
return p.interface_decl() return p.interface_decl()
} }
.key_import { .key_import {
if !p.inside_import_section {
p.error_with_pos('`import x` can only be declared at the beginning of the file', p.error_with_pos('`import x` can only be declared at the beginning of the file',
p.tok.pos()) p.tok.pos())
}
return p.import_stmt() return p.import_stmt()
} }
.key_global { .key_global {
@ -677,13 +696,15 @@ fn (mut p Parser) top_stmt() ast.Stmt {
return p.struct_decl(false) return p.struct_decl(false)
} }
.dollar { .dollar {
if p.peek_tok.kind == .eof { match p.peek_tok.kind {
.eof {
return p.unexpected(got: 'eof') return p.unexpected(got: 'eof')
} }
if p.peek_tok.kind == .key_for { .key_for {
comptime_for_stmt := p.comptime_for() comptime_for_stmt := p.comptime_for()
return p.other_stmts(comptime_for_stmt) return p.other_stmts(comptime_for_stmt)
} else if p.peek_tok.kind == .key_if { }
.key_if {
if_expr := p.if_expr(true, false) if_expr := p.if_expr(true, false)
cur_stmt := ast.ExprStmt{ cur_stmt := ast.ExprStmt{
expr: if_expr expr: if_expr
@ -694,10 +715,35 @@ fn (mut p Parser) top_stmt() ast.Stmt {
} else { } else {
return p.other_stmts(cur_stmt) return p.other_stmts(cur_stmt)
} }
}
.key_match {
mut pos := p.tok.pos()
expr := p.match_expr(true)
pos.update_last_line(p.prev_tok.line_nr)
return ast.ExprStmt{
expr: expr
pos: pos
}
}
.name {
// handles $dbg directly without registering token
if p.peek_tok.lit == 'dbg' {
return p.dbg_stmt()
} else { } else {
mut pos := p.tok.pos()
expr := p.expr(0)
pos.update_last_line(p.prev_tok.line_nr)
return ast.ExprStmt{
expr: expr
pos: pos
}
}
}
else {
return p.unexpected() return p.unexpected()
} }
} }
}
.hash { .hash {
return p.hash() return p.hash()
} }
@ -2470,7 +2516,10 @@ fn (mut p Parser) const_decl() ast.ConstDecl {
} }
mut expr := ast.Expr{} mut expr := ast.Expr{}
if !is_virtual_c_const { if !is_virtual_c_const {
old_inside_assign_rhs := p.inside_assign_rhs
p.inside_assign_rhs = true
expr = p.expr(0) expr = p.expr(0)
p.inside_assign_rhs = old_inside_assign_rhs
} }
if is_block { if is_block {
end_comments << p.eat_comments(same_line: true) end_comments << p.eat_comments(same_line: true)
@ -2596,7 +2645,10 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
mut typ_pos := token.Pos{} mut typ_pos := token.Pos{}
if has_expr { if has_expr {
p.next() // = p.next() // =
old_assign_rhs := p.inside_assign_rhs
p.inside_assign_rhs = true
expr = p.expr(0) expr = p.expr(0)
p.inside_assign_rhs = old_assign_rhs
match mut expr { match mut expr {
ast.CastExpr, ast.StructInit, ast.ArrayInit, ast.ChanInit { ast.CastExpr, ast.StructInit, ast.ArrayInit, ast.ChanInit {
typ = expr.typ typ = expr.typ
@ -2759,7 +2811,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
} }
is_pub: is_pub is_pub: is_pub
}) })
if typ in [ast.string_type_idx, ast.rune_type_idx, ast.array_type_idx, ast.map_type_idx] { if typ in [ast.string_type_idx, ast.rune_type_idx, ast.array_type_idx, ast.map_type_idx]
&& !p.pref.is_fmt {
p.error_with_pos('cannot register sum type `${name}`, another type with this name exists', p.error_with_pos('cannot register sum type `${name}`, another type with this name exists',
name_pos) name_pos)
return ast.SumTypeDecl{} return ast.SumTypeDecl{}
@ -2806,7 +2859,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
is_pub: is_pub is_pub: is_pub
}) })
type_end_pos := p.prev_tok.pos() type_end_pos := p.prev_tok.pos()
if idx in [ast.string_type_idx, ast.rune_type_idx, ast.array_type_idx, ast.map_type_idx] { if idx in [ast.string_type_idx, ast.rune_type_idx, ast.array_type_idx, ast.map_type_idx]
&& !p.pref.is_fmt {
p.error_with_pos('cannot register alias `${name}`, another type with this name exists', p.error_with_pos('cannot register alias `${name}`, another type with this name exists',
name_pos) name_pos)
return ast.AliasTypeDecl{} return ast.AliasTypeDecl{}
@ -3017,3 +3071,23 @@ fn (mut p Parser) add_defer_var(ident ast.Ident) {
} }
} }
} }
// skip `{...}`
fn (mut p Parser) skip_scope() {
mut br_cnt := 0
for {
match p.tok.kind {
.lcbr { br_cnt++ }
.rcbr { br_cnt-- }
.eof { break }
else {}
}
if br_cnt == 0 {
break
}
p.next()
}
if p.tok.kind == .rcbr {
p.next()
}
}

View file

@ -338,7 +338,10 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
if p.tok.kind == .assign { if p.tok.kind == .assign {
// Default value // Default value
p.next() p.next()
old_assign_rhs := p.inside_assign_rhs
p.inside_assign_rhs = true
default_expr = p.expr(0) default_expr = p.expr(0)
p.inside_assign_rhs = old_assign_rhs
match mut default_expr { match mut default_expr {
ast.EnumVal { default_expr.typ = typ } ast.EnumVal { default_expr.typ = typ }
// TODO: implement all types?? // TODO: implement all types??
@ -457,7 +460,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
} }
} }
// allow duplicate c struct declarations // allow duplicate c struct declarations
if ret == -1 && language != .c { if ret == -1 && language != .c && !p.pref.is_fmt {
p.error_with_pos('cannot register struct `${name}`, another type with this name exists', p.error_with_pos('cannot register struct `${name}`, another type with this name exists',
name_pos) name_pos)
return ast.StructDecl{} return ast.StructDecl{}
@ -671,7 +674,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
} }
language: language language: language
) )
if reg_idx == -1 { if reg_idx == -1 && !p.pref.is_fmt {
p.error_with_pos('cannot register interface `${interface_name}`, another type with this name exists', p.error_with_pos('cannot register interface `${interface_name}`, another type with this name exists',
name_pos) name_pos)
return ast.InterfaceDecl{} return ast.InterfaceDecl{}

View file

@ -0,0 +1,94 @@
// vtest vflags: -d new_1 -d new_a_1 -d new_b_1 -d new_c_1 -d new_d_1 -d new_e_1
module main
// this is comment, should skip
$if new_1 ? {
// this is comment, should skip
import os
// this is comment, should skip
} $else $if new_2 ? {
// this is comment, should skip
import math
// this is comment, should skip
} $else {
// this is comment, should skip
import time
// this is comment, should skip
}
// this is comment, should skip
const t = $if amd64 { 1 } $else { 2 }
$if new_a_1 ? {
pub type Digits = u64
} $else $if new_a_2 ? {
pub type Digits = u32
} $else {
pub type Digits = u8
}
$if new_b_1 ? {
pub const const1 = '123'
} $else $if new_b_2 ? {
pub const const1 = 123
} $else {
pub const const1 = 1.1
}
$if new_c_1 ? {
pub enum Enum1 {
enum1_a
enum1_b
}
} $else $if new_c_2 ? {
pub enum Enum1 {
enum1_c
enum1_d
}
} $else {
pub enum Enum1 {
enum1_e
enum1_f
}
}
$if new_d_1 ? {
pub struct Struct1 {
a int
}
} $else $if new_d_2 ? {
pub struct Struct1 {
b int
}
} $else {
pub struct Struct1 {
c int
}
}
$if new_e_1 ? {
pub fn ret() string {
return 'new_e_1'
}
} $else $if new_e_2 ? {
pub fn ret() string {
return 'new_e_2'
}
} $else {
pub fn ret() string {
return 'new_e_3'
}
}
fn test_main() {
assert os.user_os().len > 0
assert t in [1, 2]
assert sizeof(Digits) == 8 // Digits == u64
assert const1 == '123'
_ := Enum1.enum1_a // should compile
_ := Struct1{
a: 123
} // should compile
assert ret() == 'new_e_1'
}

View file

@ -0,0 +1,94 @@
// vtest vflags: -d new_2 -d new_a_2 -d new_b_2 -d new_c_2 -d new_d_2 -d new_e_2
module main
// this is comment, should skip
$if new_1 ? {
// this is comment, should skip
import os
// this is comment, should skip
} $else $if new_2 ? {
// this is comment, should skip
import math
// this is comment, should skip
} $else {
// this is comment, should skip
import time
// this is comment, should skip
}
// this is comment, should skip
const t = $if amd64 { 1 } $else { 2 }
$if new_a_1 ? {
pub type Digits = u64
} $else $if new_a_2 ? {
pub type Digits = u32
} $else {
pub type Digits = u8
}
$if new_b_1 ? {
pub const const1 = '123'
} $else $if new_b_2 ? {
pub const const1 = 123
} $else {
pub const const1 = 1.1
}
$if new_c_1 ? {
pub enum Enum1 {
enum1_a
enum1_b
}
} $else $if new_c_2 ? {
pub enum Enum1 {
enum1_c
enum1_d
}
} $else {
pub enum Enum1 {
enum1_e
enum1_f
}
}
$if new_d_1 ? {
pub struct Struct1 {
a int
}
} $else $if new_d_2 ? {
pub struct Struct1 {
b int
}
} $else {
pub struct Struct1 {
c int
}
}
$if new_e_1 ? {
pub fn ret() string {
return 'new_e_1'
}
} $else $if new_e_2 ? {
pub fn ret() string {
return 'new_e_2'
}
} $else {
pub fn ret() string {
return 'new_e_3'
}
}
fn test_main() {
assert math.max(1, 2) == 2
assert t in [1, 2]
assert sizeof(Digits) == 4 // Digits == u32
assert const1 == 123
_ := Enum1.enum1_d // should compile
_ := Struct1{
b: 123
} // should compile
assert ret() == 'new_e_2'
}

View file

@ -0,0 +1,94 @@
// vtest vflags: -d new_3 -d new_a_3 -d new_b_3 -d new_c_3 -d new_d_3 -d new_e_3
module main
// this is comment, should skip
$if new_1 ? {
// this is comment, should skip
import os
// this is comment, should skip
} $else $if new_2 ? {
// this is comment, should skip
import math
// this is comment, should skip
} $else {
// this is comment, should skip
import time
// this is comment, should skip
}
// this is comment, should skip
const t = $if amd64 { 1 } $else { 2 }
$if new_a_1 ? {
pub type Digits = u64
} $else $if new_a_2 ? {
pub type Digits = u32
} $else {
pub type Digits = u8
}
$if new_b_1 ? {
pub const const1 = '123'
} $else $if new_b_2 ? {
pub const const1 = 123
} $else {
pub const const1 = 1.1
}
$if new_c_1 ? {
pub enum Enum1 {
enum1_a
enum1_b
}
} $else $if new_c_2 ? {
pub enum Enum1 {
enum1_c
enum1_d
}
} $else {
pub enum Enum1 {
enum1_e
enum1_f
}
}
$if new_d_1 ? {
pub struct Struct1 {
a int
}
} $else $if new_d_2 ? {
pub struct Struct1 {
b int
}
} $else {
pub struct Struct1 {
c int
}
}
$if new_e_1 ? {
pub fn ret() string {
return 'new_e_1'
}
} $else $if new_e_2 ? {
pub fn ret() string {
return 'new_e_2'
}
} $else {
pub fn ret() string {
return 'new_e_3'
}
}
fn test_main() {
assert time.days_in_year == 365
assert t in [1, 2]
assert sizeof(Digits) == 1 // Digits == u8
assert const1 == 1.1
_ := Enum1.enum1_e // should compile
_ := Struct1{
c: 123
} // should compile
assert ret() == 'new_e_3'
}