mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
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
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:
parent
344f7ccdcb
commit
19d31f221f
14 changed files with 955 additions and 434 deletions
|
@ -1,5 +1,7 @@
|
|||
module ast
|
||||
|
||||
import v.pref
|
||||
|
||||
pub const valid_comptime_if_os = ['windows', 'ios', 'macos', 'mach', 'darwin', 'hpux', 'gnu', 'qnx',
|
||||
'linux', 'freebsd', 'openbsd', 'netbsd', 'bsd', 'dragonfly', 'android', 'termux', 'solaris',
|
||||
'haiku', 'serenity', 'vinix', 'plan9', 'wasm32_emscripten']
|
||||
|
@ -22,3 +24,225 @@ fn all_valid_comptime_idents() []string {
|
|||
res << valid_comptime_if_other
|
||||
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}`')
|
||||
}
|
||||
|
|
|
@ -784,191 +784,6 @@ fn (mut c Checker) evaluate_once_comptime_if_attribute(mut node ast.Attr) bool {
|
|||
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`
|
||||
fn (mut c Checker) is_generic_ident(ident string) bool {
|
||||
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
|
||||
is_user_ident = true
|
||||
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})')
|
||||
is_true = cname in c.pref.compile_defines
|
||||
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
|
||||
is_user_ident = false
|
||||
ident_name = cname
|
||||
if cname in ast.valid_comptime_if_os {
|
||||
if cname_enum_val := pref.os_from_string(cname) {
|
||||
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' {
|
||||
if cname in ast.valid_comptime_not_user_defined {
|
||||
if cname == 'threads' {
|
||||
is_true = c.table.gostmts > 0
|
||||
}
|
||||
'js_node' {
|
||||
is_true = c.pref.backend == .js_node
|
||||
}
|
||||
'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)
|
||||
} else {
|
||||
is_true = ast.eval_comptime_not_user_defined_ident(cname, c.pref) or {
|
||||
c.error(err.msg(), cond.pos)
|
||||
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)
|
||||
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})')
|
||||
} else {
|
||||
sb.write_string('${is_true}')
|
||||
|
|
|
@ -51,6 +51,8 @@ pub mut:
|
|||
wsinfix_depth int
|
||||
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.
|
||||
global_processed_imports []string
|
||||
branch_processed_imports []string
|
||||
}
|
||||
|
||||
@[params]
|
||||
|
@ -317,12 +319,17 @@ pub fn (mut f Fmt) import_stmt(imp ast.Import) {
|
|||
return
|
||||
}
|
||||
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.
|
||||
f.import_comments(imp.next_comments)
|
||||
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 {
|
||||
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`.
|
||||
|
@ -1070,7 +1077,7 @@ pub fn (mut f Fmt) enum_decl(node ast.EnumDecl) {
|
|||
f.writeln('')
|
||||
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) {
|
||||
|
@ -2354,6 +2361,7 @@ pub fn (mut f Fmt) if_expr(node ast.IfExpr) {
|
|||
start_len := f.line_len
|
||||
for {
|
||||
for i, branch in node.branches {
|
||||
f.branch_processed_imports.clear()
|
||||
mut sum_len := 0
|
||||
if i > 0 {
|
||||
// `else`, close previous branch
|
||||
|
|
16
vlib/v/fmt/tests/comptime_if_top_enum_empty_line_keep.vv
Normal file
16
vlib/v/fmt/tests/comptime_if_top_enum_empty_line_keep.vv
Normal 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
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ pub enum PubEnum {
|
|||
foo
|
||||
bar
|
||||
}
|
||||
|
||||
enum PrivateEnum {
|
||||
foo
|
||||
bar
|
||||
|
|
|
@ -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) {
|
||||
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 {
|
||||
for node in ast_files[i].stmts {
|
||||
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) {
|
||||
for node in stmts {
|
||||
match node {
|
||||
ast.FnDecl {
|
||||
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
|
||||
}
|
||||
}
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -96,7 +96,10 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||
// p.warn('enum val $val')
|
||||
if p.tok.kind == .assign {
|
||||
p.next()
|
||||
old_assign_rhs := p.inside_assign_rhs
|
||||
p.inside_assign_rhs = true
|
||||
expr = p.expr(0)
|
||||
p.inside_assign_rhs = old_assign_rhs
|
||||
has_expr = true
|
||||
uses_exprs = true
|
||||
}
|
||||
|
@ -225,7 +228,8 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||
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',
|
||||
end_pos)
|
||||
}
|
||||
|
|
|
@ -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() }
|
||||
name = check_name
|
||||
}
|
||||
|
||||
if language == .v && !p.pref.translated && !p.is_translated && !p.builtin_mod
|
||||
&& util.contains_capital(check_name) {
|
||||
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
|
||||
keep_fn_name := p.cur_fn_name
|
||||
p.cur_fn_name = name
|
||||
mut stmts := []ast.Stmt{}
|
||||
body_start_pos := p.tok.pos()
|
||||
|
@ -671,6 +673,7 @@ run them via `v file.v` instead',
|
|||
p.inside_unsafe_fn = false
|
||||
p.inside_fn = false
|
||||
}
|
||||
p.cur_fn_name = keep_fn_name
|
||||
if !no_body && are_params_type_only {
|
||||
p.error_with_pos('functions with type only params can not have bodies', body_start_pos)
|
||||
return ast.FnDecl{
|
||||
|
|
|
@ -5,6 +5,7 @@ module parser
|
|||
|
||||
import v.ast
|
||||
import v.token
|
||||
import v.pkgconfig
|
||||
|
||||
fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
|
||||
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 comments := []ast.Comment{}
|
||||
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] {
|
||||
p.inside_if = true
|
||||
if is_comptime {
|
||||
|
@ -58,6 +61,16 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
|
|||
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{
|
||||
stmts: p.parse_block_no_scope(false)
|
||||
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
|
||||
scope: p.scope
|
||||
}
|
||||
}
|
||||
p.close_scope()
|
||||
comments = []
|
||||
break
|
||||
|
@ -114,7 +128,10 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
|
|||
comments << p.eat_comments()
|
||||
p.check(.decl_assign)
|
||||
comments << p.eat_comments()
|
||||
old_assign_rhs := p.inside_assign_rhs
|
||||
p.inside_assign_rhs = true
|
||||
expr := p.expr(0)
|
||||
p.inside_assign_rhs = old_assign_rhs
|
||||
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',
|
||||
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.inside_if_cond = true
|
||||
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 cond.op in [.key_is, .not_is] {
|
||||
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{}
|
||||
}
|
||||
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)
|
||||
branches << ast.IfBranch{
|
||||
cond: cond
|
||||
|
@ -179,6 +212,7 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
|
|||
comments: comments
|
||||
scope: p.scope
|
||||
}
|
||||
}
|
||||
p.close_scope()
|
||||
if is_guard {
|
||||
p.close_scope()
|
||||
|
@ -570,3 +604,167 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ mut:
|
|||
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
|
||||
is_vls bool
|
||||
inside_import_section bool
|
||||
pub mut:
|
||||
scanner &scanner.Scanner = unsafe { nil }
|
||||
table &ast.Table = unsafe { nil }
|
||||
|
@ -290,6 +291,7 @@ pub fn (mut p Parser) parse() &ast.File {
|
|||
} else {
|
||||
stmts << module_decl
|
||||
}
|
||||
p.inside_import_section = true
|
||||
// imports
|
||||
for {
|
||||
if p.tok.kind == .key_import {
|
||||
|
@ -451,6 +453,12 @@ fn (mut p Parser) parse_block() []ast.Stmt {
|
|||
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 {
|
||||
p.check(.lcbr)
|
||||
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 {
|
||||
mut count := 0
|
||||
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)
|
||||
}
|
||||
count++
|
||||
if count % 100000 == 0 {
|
||||
if p.is_vls {
|
||||
|
@ -617,6 +630,10 @@ fn (p &Parser) trace_parser(label string) {
|
|||
fn (mut p Parser) top_stmt() ast.Stmt {
|
||||
p.trace_parser('top_stmt')
|
||||
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 {
|
||||
.key_pub {
|
||||
match p.peek_tok.kind {
|
||||
|
@ -660,8 +677,10 @@ fn (mut p Parser) top_stmt() ast.Stmt {
|
|||
return p.interface_decl()
|
||||
}
|
||||
.key_import {
|
||||
if !p.inside_import_section {
|
||||
p.error_with_pos('`import x` can only be declared at the beginning of the file',
|
||||
p.tok.pos())
|
||||
}
|
||||
return p.import_stmt()
|
||||
}
|
||||
.key_global {
|
||||
|
@ -677,13 +696,15 @@ fn (mut p Parser) top_stmt() ast.Stmt {
|
|||
return p.struct_decl(false)
|
||||
}
|
||||
.dollar {
|
||||
if p.peek_tok.kind == .eof {
|
||||
match p.peek_tok.kind {
|
||||
.eof {
|
||||
return p.unexpected(got: 'eof')
|
||||
}
|
||||
if p.peek_tok.kind == .key_for {
|
||||
.key_for {
|
||||
comptime_for_stmt := p.comptime_for()
|
||||
return p.other_stmts(comptime_for_stmt)
|
||||
} else if p.peek_tok.kind == .key_if {
|
||||
}
|
||||
.key_if {
|
||||
if_expr := p.if_expr(true, false)
|
||||
cur_stmt := ast.ExprStmt{
|
||||
expr: if_expr
|
||||
|
@ -694,10 +715,35 @@ fn (mut p Parser) top_stmt() ast.Stmt {
|
|||
} else {
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
.hash {
|
||||
return p.hash()
|
||||
}
|
||||
|
@ -2470,7 +2516,10 @@ fn (mut p Parser) const_decl() ast.ConstDecl {
|
|||
}
|
||||
mut expr := ast.Expr{}
|
||||
if !is_virtual_c_const {
|
||||
old_inside_assign_rhs := p.inside_assign_rhs
|
||||
p.inside_assign_rhs = true
|
||||
expr = p.expr(0)
|
||||
p.inside_assign_rhs = old_inside_assign_rhs
|
||||
}
|
||||
if is_block {
|
||||
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{}
|
||||
if has_expr {
|
||||
p.next() // =
|
||||
old_assign_rhs := p.inside_assign_rhs
|
||||
p.inside_assign_rhs = true
|
||||
expr = p.expr(0)
|
||||
p.inside_assign_rhs = old_assign_rhs
|
||||
match mut expr {
|
||||
ast.CastExpr, ast.StructInit, ast.ArrayInit, ast.ChanInit {
|
||||
typ = expr.typ
|
||||
|
@ -2759,7 +2811,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
|||
}
|
||||
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',
|
||||
name_pos)
|
||||
return ast.SumTypeDecl{}
|
||||
|
@ -2806,7 +2859,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
|||
is_pub: is_pub
|
||||
})
|
||||
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',
|
||||
name_pos)
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -338,7 +338,10 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
|||
if p.tok.kind == .assign {
|
||||
// Default value
|
||||
p.next()
|
||||
old_assign_rhs := p.inside_assign_rhs
|
||||
p.inside_assign_rhs = true
|
||||
default_expr = p.expr(0)
|
||||
p.inside_assign_rhs = old_assign_rhs
|
||||
match mut default_expr {
|
||||
ast.EnumVal { default_expr.typ = typ }
|
||||
// TODO: implement all types??
|
||||
|
@ -457,7 +460,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
|||
}
|
||||
}
|
||||
// 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',
|
||||
name_pos)
|
||||
return ast.StructDecl{}
|
||||
|
@ -671,7 +674,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
}
|
||||
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',
|
||||
name_pos)
|
||||
return ast.InterfaceDecl{}
|
||||
|
|
94
vlib/v/tests/comptime/comptime_if_top_1_test.v
Normal file
94
vlib/v/tests/comptime/comptime_if_top_1_test.v
Normal 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'
|
||||
}
|
94
vlib/v/tests/comptime/comptime_if_top_2_test.v
Normal file
94
vlib/v/tests/comptime/comptime_if_top_2_test.v
Normal 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'
|
||||
}
|
94
vlib/v/tests/comptime/comptime_if_top_3_test.v
Normal file
94
vlib/v/tests/comptime/comptime_if_top_3_test.v
Normal 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'
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue