mirror of
https://github.com/vlang/v.git
synced 2025-09-17 11:26:17 -04:00
v: $dbg statement - native V debugger REPL (#20533)
This commit is contained in:
parent
45e13ea02a
commit
9f6448e30e
@ -440,6 +440,7 @@ fn (t Tree) stmt(node ast.Stmt) &Node {
|
|||||||
ast.AsmStmt { return t.asm_stmt(node) }
|
ast.AsmStmt { return t.asm_stmt(node) }
|
||||||
ast.NodeError { return t.node_error(node) }
|
ast.NodeError { return t.node_error(node) }
|
||||||
ast.EmptyStmt { return t.empty_stmt(node) }
|
ast.EmptyStmt { return t.empty_stmt(node) }
|
||||||
|
ast.DebuggerStmt { return t.debugger_stmt(node) }
|
||||||
}
|
}
|
||||||
return t.null_node()
|
return t.null_node()
|
||||||
}
|
}
|
||||||
@ -1932,6 +1933,13 @@ fn (t Tree) empty_stmt(node ast.EmptyStmt) &Node {
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (t Tree) debugger_stmt(node ast.DebuggerStmt) &Node {
|
||||||
|
mut obj := new_object()
|
||||||
|
obj.add_terse('ast_type', t.string_node('DebuggerStmt'))
|
||||||
|
obj.add('pos', t.pos(node.pos))
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
fn (t Tree) nil_expr(node ast.Nil) &Node {
|
fn (t Tree) nil_expr(node ast.Nil) &Node {
|
||||||
mut obj := new_object()
|
mut obj := new_object()
|
||||||
obj.add_terse('ast_type', t.string_node('Nil'))
|
obj.add_terse('ast_type', t.string_node('Nil'))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
module builtin
|
module builtin
|
||||||
|
|
||||||
fn print_backtrace_skipping_top_frames(xskipframes int) bool {
|
// print_backtrace_skipping_top_frames prints the backtrace skipping N top frames
|
||||||
|
pub fn print_backtrace_skipping_top_frames(xskipframes int) bool {
|
||||||
$if no_backtrace ? {
|
$if no_backtrace ? {
|
||||||
return false
|
return false
|
||||||
} $else {
|
} $else {
|
||||||
|
@ -61,7 +61,8 @@ const symopt_include_32bit_modules = 0x00002000
|
|||||||
const symopt_allow_zero_address = 0x01000000
|
const symopt_allow_zero_address = 0x01000000
|
||||||
const symopt_debug = u32(0x80000000)
|
const symopt_debug = u32(0x80000000)
|
||||||
|
|
||||||
fn print_backtrace_skipping_top_frames(skipframes int) bool {
|
// print_backtrace_skipping_top_frames prints the backtrace skipping N top frames
|
||||||
|
pub fn print_backtrace_skipping_top_frames(skipframes int) bool {
|
||||||
$if msvc {
|
$if msvc {
|
||||||
return print_backtrace_skipping_top_frames_msvc(skipframes)
|
return print_backtrace_skipping_top_frames_msvc(skipframes)
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ pub type Stmt = AsmStmt
|
|||||||
| BranchStmt
|
| BranchStmt
|
||||||
| ComptimeFor
|
| ComptimeFor
|
||||||
| ConstDecl
|
| ConstDecl
|
||||||
|
| DebuggerStmt
|
||||||
| DeferStmt
|
| DeferStmt
|
||||||
| EmptyStmt
|
| EmptyStmt
|
||||||
| EnumDecl
|
| EnumDecl
|
||||||
@ -1721,6 +1722,11 @@ pub const riscv_with_number_register_list = {
|
|||||||
'a#': 8
|
'a#': 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DebuggerStmt {
|
||||||
|
pub:
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
// `assert a == 0, 'a is zero'`
|
// `assert a == 0, 'a is zero'`
|
||||||
@[minify]
|
@[minify]
|
||||||
pub struct AssertStmt {
|
pub struct AssertStmt {
|
||||||
|
@ -182,6 +182,20 @@ pub fn (s &Scope) innermost(pos int) &Scope {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get_all_vars extracts all current scope vars
|
||||||
|
pub fn (s &Scope) get_all_vars() []ScopeObject {
|
||||||
|
mut scope_vars := []ScopeObject{}
|
||||||
|
for sc := unsafe { s }; true; sc = sc.parent {
|
||||||
|
if sc.objects.len > 0 {
|
||||||
|
scope_vars << sc.objects.values().filter(|it| it is Var)
|
||||||
|
}
|
||||||
|
if sc.dont_lookup_parent() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scope_vars
|
||||||
|
}
|
||||||
|
|
||||||
@[inline]
|
@[inline]
|
||||||
pub fn (s &Scope) contains(pos int) bool {
|
pub fn (s &Scope) contains(pos int) bool {
|
||||||
return pos >= s.start_pos && pos <= s.end_pos
|
return pos >= s.start_pos && pos <= s.end_pos
|
||||||
|
@ -2000,6 +2000,7 @@ fn (mut c Checker) stmt(mut node ast.Stmt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.NodeError {}
|
ast.NodeError {}
|
||||||
|
ast.DebuggerStmt {}
|
||||||
ast.AsmStmt {
|
ast.AsmStmt {
|
||||||
c.asm_stmt(mut node)
|
c.asm_stmt(mut node)
|
||||||
}
|
}
|
||||||
|
284
vlib/v/debug/debug.v
Normal file
284
vlib/v/debug/debug.v
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
// Copyright (c) 2019-2024 Felipe Pena. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
|
||||||
|
@[has_globals]
|
||||||
|
module debug
|
||||||
|
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
import readline
|
||||||
|
import strings
|
||||||
|
|
||||||
|
__global g_debugger = Debugger{}
|
||||||
|
|
||||||
|
// Debugger holds the V debug information for REPL
|
||||||
|
@[heap]
|
||||||
|
struct Debugger {
|
||||||
|
mut:
|
||||||
|
is_tty bool = os.is_atty(0) > 0 // is tty?
|
||||||
|
exited bool // user exiting flag
|
||||||
|
last_cmd string // save the last cmd
|
||||||
|
last_args string // save the last args
|
||||||
|
watch_vars []string // save the watched vars
|
||||||
|
cmdline readline.Readline = readline.Readline{
|
||||||
|
completion_list: [
|
||||||
|
'anon?',
|
||||||
|
'bt',
|
||||||
|
'continue',
|
||||||
|
'generic?',
|
||||||
|
'heap',
|
||||||
|
'help',
|
||||||
|
'list',
|
||||||
|
'mem',
|
||||||
|
'memory',
|
||||||
|
'method?',
|
||||||
|
'mod',
|
||||||
|
'print',
|
||||||
|
'quit',
|
||||||
|
'scope',
|
||||||
|
'unwatch',
|
||||||
|
'watch',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugContextVar holds the scope variable information
|
||||||
|
pub struct DebugContextVar {
|
||||||
|
name string // var name
|
||||||
|
typ string // its type name
|
||||||
|
value string // its str value
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugContextInfo has the context info for the debugger repl
|
||||||
|
pub struct DebugContextInfo {
|
||||||
|
is_anon bool // cur fn is anon?
|
||||||
|
is_generic bool // cur fn is a generic?
|
||||||
|
is_method bool // cur fn is a bool?
|
||||||
|
receiver_typ_name string // cur receiver type name (method only)
|
||||||
|
line int // cur line number
|
||||||
|
file string // cur file name
|
||||||
|
mod string // cur module name
|
||||||
|
fn_name string // cur function name
|
||||||
|
scope map[string]DebugContextVar // scope var data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_println(s string) {
|
||||||
|
println(s)
|
||||||
|
flush_stdout()
|
||||||
|
}
|
||||||
|
|
||||||
|
// show_variable prints the variable info if found into the cur context
|
||||||
|
fn (d DebugContextInfo) show_variable(var_name string, is_watch bool) {
|
||||||
|
if info := d.scope[var_name] {
|
||||||
|
flush_println('${var_name} = ${info.value} (${info.typ})')
|
||||||
|
} else if !is_watch {
|
||||||
|
eprintln('[error] var `${var_name}` not found')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (d DebugContextInfo) show_watched_vars(watch_vars []string) {
|
||||||
|
for var in watch_vars {
|
||||||
|
d.show_variable(var, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// show_scope prints the cur context scope variables
|
||||||
|
fn (d DebugContextInfo) show_scope() {
|
||||||
|
for k, v in d.scope {
|
||||||
|
flush_println('${k} = ${v.value} (${v.typ})')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugContextInfo.ctx displays info about the current fn context
|
||||||
|
fn (d DebugContextInfo) ctx() string {
|
||||||
|
mut s := strings.new_builder(512)
|
||||||
|
if d.is_method {
|
||||||
|
s.write_string('[${d.mod}] (${d.receiver_typ_name}) ${d.fn_name}')
|
||||||
|
} else {
|
||||||
|
s.write_string('[${d.mod}] ${d.fn_name}')
|
||||||
|
}
|
||||||
|
return s.str()
|
||||||
|
}
|
||||||
|
|
||||||
|
// print_help prints the debugger REPL commands help
|
||||||
|
fn (mut d Debugger) print_help() {
|
||||||
|
println('vdbg commands:')
|
||||||
|
println(' anon?\t\t\tcheck if the current context is anon')
|
||||||
|
println(' bt\t\t\tprints a backtrace')
|
||||||
|
println(' c, continue\t\tcontinue debugging')
|
||||||
|
println(' generic?\t\tcheck if the current context is generic')
|
||||||
|
println(' heap\t\t\tshow heap memory usage')
|
||||||
|
println(' h, help, ?\t\tshow this help')
|
||||||
|
println(' l, list [lines]\tshow some lines from current break (default: 3)')
|
||||||
|
println(' mem, memory\t\tshow memory usage')
|
||||||
|
println(' method?\t\tcheck if the current context is a method')
|
||||||
|
println(' m, mod\t\tshow current module name')
|
||||||
|
println(' p, print <var>\tprints an variable')
|
||||||
|
println(' q, quit\t\texits debugging session in the code')
|
||||||
|
println(' scope\t\t\tshow the vars in the current scope')
|
||||||
|
println(' u, unwatch <var>\tunwatches a variable')
|
||||||
|
println(' w, watch <var>\twatches a variable')
|
||||||
|
flush_println('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// read_line provides the user prompt based on tty flag
|
||||||
|
fn (mut d Debugger) read_line(prompt string) (string, bool) {
|
||||||
|
if d.is_tty {
|
||||||
|
mut is_ctrl := false
|
||||||
|
line := d.cmdline.read_line(prompt) or {
|
||||||
|
is_ctrl = true
|
||||||
|
''
|
||||||
|
}
|
||||||
|
return line.trim_right('\r\n '), is_ctrl
|
||||||
|
} else {
|
||||||
|
print(prompt)
|
||||||
|
flush_stdout()
|
||||||
|
return os.get_raw_line().trim_right('\r\n '), false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut d Debugger) parse_input(input string, is_ctrl bool) (string, string) {
|
||||||
|
splitted := input.split(' ')
|
||||||
|
if !is_ctrl && splitted[0] == '' {
|
||||||
|
return d.last_cmd, d.last_args
|
||||||
|
} else {
|
||||||
|
cmd := if is_ctrl { d.last_cmd } else { splitted[0] }
|
||||||
|
args := if splitted.len > 1 { splitted[1] } else { '' }
|
||||||
|
d.last_cmd = cmd
|
||||||
|
d.last_args = args
|
||||||
|
return cmd, args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print_context_lines prints N lines before and after the current location
|
||||||
|
fn (mut d Debugger) print_context_lines(path string, line int, lines int) ! {
|
||||||
|
file_content := os.read_file(path)!
|
||||||
|
chunks := file_content.split('\n')
|
||||||
|
offset := math.max(line - lines, 1)
|
||||||
|
for n, s in chunks[offset - 1..math.min(chunks.len, line + lines)] {
|
||||||
|
ind := if n + offset == line { '>' } else { ' ' }
|
||||||
|
flush_println('${n + offset:04}${ind} ${s}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print_memory_use prints the GC memory use
|
||||||
|
fn (d &Debugger) print_memory_use() {
|
||||||
|
flush_println(gc_memory_use().str())
|
||||||
|
}
|
||||||
|
|
||||||
|
// print_heap_usage prints the GC heap usage
|
||||||
|
fn (d &Debugger) print_heap_usage() {
|
||||||
|
h := gc_heap_usage()
|
||||||
|
flush_println('heap size: ${h.heap_size}')
|
||||||
|
flush_println('free bytes: ${h.free_bytes}')
|
||||||
|
flush_println('total bytes: ${h.total_bytes}')
|
||||||
|
}
|
||||||
|
|
||||||
|
// watch_var adds a variable to watch_list
|
||||||
|
fn (mut d Debugger) watch_var(var string) bool {
|
||||||
|
if var !in d.watch_vars {
|
||||||
|
d.watch_vars << var
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwatch_var removes a variable from watch list
|
||||||
|
fn (mut d Debugger) unwatch_var(var string) {
|
||||||
|
item := d.watch_vars.index(var)
|
||||||
|
if item >= 0 {
|
||||||
|
d.watch_vars.delete(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// interact displays the V debugger REPL for user interaction
|
||||||
|
@[markused]
|
||||||
|
pub fn (mut d Debugger) interact(info DebugContextInfo) ! {
|
||||||
|
if d.exited {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flush_println('Break on ${info.ctx()} in ${info.file}:${info.line}')
|
||||||
|
if d.watch_vars.len > 0 {
|
||||||
|
info.show_watched_vars(d.watch_vars)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
input, is_ctrl := d.read_line('${info.file}:${info.line} vdbg> ')
|
||||||
|
cmd, args := d.parse_input(input, is_ctrl)
|
||||||
|
match cmd {
|
||||||
|
'anon?' {
|
||||||
|
flush_println(info.is_anon.str())
|
||||||
|
}
|
||||||
|
'bt' {
|
||||||
|
print_backtrace_skipping_top_frames(2)
|
||||||
|
flush_stdout()
|
||||||
|
}
|
||||||
|
'c', 'continue' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
'generic?' {
|
||||||
|
flush_println(info.is_generic.str())
|
||||||
|
}
|
||||||
|
'heap' {
|
||||||
|
d.print_heap_usage()
|
||||||
|
}
|
||||||
|
'?', 'h', 'help' {
|
||||||
|
d.print_help()
|
||||||
|
}
|
||||||
|
'l', 'list' {
|
||||||
|
lines := if args != '' { args.int() } else { 3 }
|
||||||
|
if lines < 0 {
|
||||||
|
eprintln('[error] cannot use negative line amount')
|
||||||
|
} else {
|
||||||
|
d.print_context_lines(info.file, info.line, lines)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'method?' {
|
||||||
|
flush_println(info.is_method.str())
|
||||||
|
}
|
||||||
|
'mem', 'memory' {
|
||||||
|
d.print_memory_use()
|
||||||
|
}
|
||||||
|
'm', 'mod' {
|
||||||
|
flush_println(info.mod)
|
||||||
|
}
|
||||||
|
'p', 'print' {
|
||||||
|
if args == '' {
|
||||||
|
eprintln('[error] var name is expected as parameter')
|
||||||
|
} else {
|
||||||
|
info.show_variable(args, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'scope' {
|
||||||
|
info.show_scope()
|
||||||
|
}
|
||||||
|
'q', 'quit' {
|
||||||
|
d.exited = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
'u', 'unwatch' {
|
||||||
|
if args == '' {
|
||||||
|
eprintln('[error] var name is expected as parameter')
|
||||||
|
} else {
|
||||||
|
d.unwatch_var(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'w', 'watch' {
|
||||||
|
if args == '' {
|
||||||
|
eprintln('[error] var name is expected as parameter')
|
||||||
|
} else {
|
||||||
|
if d.watch_var(args) {
|
||||||
|
info.show_variable(args, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'' {
|
||||||
|
if is_ctrl {
|
||||||
|
flush_println('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
eprintln('unknown command `${cmd}`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
vlib/v/debug/interactive_test.v
Normal file
59
vlib/v/debug/interactive_test.v
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import os
|
||||||
|
import term
|
||||||
|
import time
|
||||||
|
|
||||||
|
const vexe = @VEXE
|
||||||
|
const expect_tests_path = os.join_path(@VEXEROOT, 'vlib', 'v', 'debug', 'tests')
|
||||||
|
const test_module_path = os.join_path(os.vtmp_dir(), 'test_vdbg_input')
|
||||||
|
const bar = term.yellow('-'.repeat(100))
|
||||||
|
|
||||||
|
const expect_exe = os.quoted_path(os.find_abs_path_of_executable('expect') or {
|
||||||
|
eprintln('skipping test, since expect is missing')
|
||||||
|
exit(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
fn testsuite_begin() {
|
||||||
|
os.chdir(@VEXEROOT) or {}
|
||||||
|
os.rmdir_all(test_module_path) or {}
|
||||||
|
os.mkdir_all(test_module_path) or {}
|
||||||
|
dump(test_module_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gprintln(msg string) {
|
||||||
|
println(term.gray(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_debugger() {
|
||||||
|
os.chdir(test_module_path)!
|
||||||
|
all_expect_files := os.walk_ext(expect_tests_path, '.expect')
|
||||||
|
assert all_expect_files.len > 0, 'no .expect files found in ${expect_tests_path}'
|
||||||
|
for eidx, efile in all_expect_files {
|
||||||
|
vfile := efile.replace('.expect', '.vv')
|
||||||
|
output_file := os.join_path(test_module_path, os.file_name(efile).replace('.expect',
|
||||||
|
'.exe'))
|
||||||
|
|
||||||
|
println(bar)
|
||||||
|
gprintln('>>>> running test [${eidx + 1}/${all_expect_files.len}], ${term.magenta(efile)} ...')
|
||||||
|
|
||||||
|
compile_sw := time.new_stopwatch()
|
||||||
|
comp_res := os.system('${os.quoted_path(vexe)} -o ${os.quoted_path(output_file)} ${os.quoted_path(vfile)}')
|
||||||
|
gprintln('>>>>>>>>>>> compilation took ${compile_sw.elapsed().milliseconds()} ms, comp_res: ${comp_res}')
|
||||||
|
|
||||||
|
expect_cmd := '${expect_exe} -d -c "set stty_init {rows 24 cols 80}" -c "set timeout 15" ${os.quoted_path(efile)} ${os.quoted_path(output_file)} ${os.quoted_path(vfile)}'
|
||||||
|
println(term.cyan(expect_cmd))
|
||||||
|
flush_stdout()
|
||||||
|
sw := time.new_stopwatch()
|
||||||
|
res := os.system(expect_cmd)
|
||||||
|
gprintln('>>>>>>>>>>> expect took: ${sw.elapsed().milliseconds()} ms, res: ${res}')
|
||||||
|
|
||||||
|
if res != 0 {
|
||||||
|
assert false, term.red('failed expect cmd: ${expect_cmd}')
|
||||||
|
}
|
||||||
|
assert true
|
||||||
|
}
|
||||||
|
os.chdir(@VEXEROOT) or {}
|
||||||
|
os.rmdir_all(test_module_path) or {}
|
||||||
|
|
||||||
|
println(bar)
|
||||||
|
gprintln(term.bold('A total of ${all_expect_files.len} tests for the debugger are OK'))
|
||||||
|
}
|
9
vlib/v/debug/tests/aggregate.expect
Normal file
9
vlib/v/debug/tests/aggregate.expect
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect -ex "\[${test_file}:25\] a.a: 123\r\n"
|
||||||
|
expect -ex "Break on \[main\] main in ${test_file}:26\r\n"
|
||||||
|
expect -ex "${test_file}:26 vdbg> " { send "p a\n" } timeout { exit 1 }
|
||||||
|
expect -ex "a = Test{\r\n a: 123\r\n} ((main.main.Test | main.main.Test2))" { send "q\n" } timeout { exit 1 }
|
||||||
|
expect eof
|
30
vlib/v/debug/tests/aggregate.vv
Normal file
30
vlib/v/debug/tests/aggregate.vv
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
struct Test {
|
||||||
|
a int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Test2 {
|
||||||
|
a int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Test3 {
|
||||||
|
a int
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITest {
|
||||||
|
a int
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestSum = Test | Test2 | Test3
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := TestSum(Test{
|
||||||
|
a: 123
|
||||||
|
})
|
||||||
|
match a {
|
||||||
|
Test, Test2 {
|
||||||
|
dump(a.a)
|
||||||
|
$dbg;
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
10
vlib/v/debug/tests/comptime_smartcast.expect
Normal file
10
vlib/v/debug/tests/comptime_smartcast.expect
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect -ex "Break on \[main\] comptime_smartcast in ${test_file}:3\r\n"
|
||||||
|
expect "${test_file}:3 vdbg> " { send "p v\n" } timeout { exit 1 }
|
||||||
|
expect "v = 1 (int)" { send "c\n" } timeout { exit 1 }
|
||||||
|
expect "${test_file}:5 vdbg> " { send "p v\n" } timeout { exit 1 }
|
||||||
|
expect "v = true (bool)" { send "q\n" } timeout { exit 1 }
|
||||||
|
expect eof
|
12
vlib/v/debug/tests/comptime_smartcast.vv
Normal file
12
vlib/v/debug/tests/comptime_smartcast.vv
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
fn comptime_smartcast[T](v T) {
|
||||||
|
$if v is int {
|
||||||
|
$dbg;
|
||||||
|
} $else {
|
||||||
|
$dbg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
comptime_smartcast(1)
|
||||||
|
comptime_smartcast(true)
|
||||||
|
}
|
8
vlib/v/debug/tests/comptime_variant.expect
Normal file
8
vlib/v/debug/tests/comptime_variant.expect
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect -ex "Break on \[main\] comptime_variant_int in ${test_file}:7"
|
||||||
|
expect -ex "${test_file}:7 vdbg> " { send "p a\n" } timeout { exit 1 }
|
||||||
|
expect -ex "a = 0 (int)" { send "q\n" } timeout { exit 1 }
|
||||||
|
expect eof
|
15
vlib/v/debug/tests/comptime_variant.vv
Normal file
15
vlib/v/debug/tests/comptime_variant.vv
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
type MySum = int | string
|
||||||
|
|
||||||
|
fn comptime_variant_int() {
|
||||||
|
a := MySum(int(0))
|
||||||
|
$for v in MySum.variants {
|
||||||
|
if a is v {
|
||||||
|
$dbg;
|
||||||
|
dump(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
comptime_variant_int()
|
||||||
|
}
|
8
vlib/v/debug/tests/interface_var.expect
Normal file
8
vlib/v/debug/tests/interface_var.expect
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect -ex "Break on \[main\] interface_var in ${test_file}:14\r\n"
|
||||||
|
expect -ex "${test_file}:14 vdbg> " { send "p a\n" } timeout { exit 1 }
|
||||||
|
expect -ex "a = Test{\r\n a: MySum(true)" { send "q\n" } timeout { exit 1 }
|
||||||
|
expect eof
|
22
vlib/v/debug/tests/interface_var.vv
Normal file
22
vlib/v/debug/tests/interface_var.vv
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
interface ITest {
|
||||||
|
a MySum
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Test {
|
||||||
|
a MySum
|
||||||
|
}
|
||||||
|
|
||||||
|
type MySum = bool | int
|
||||||
|
|
||||||
|
fn interface_var(a ITest) {
|
||||||
|
match a {
|
||||||
|
Test {
|
||||||
|
$dbg;
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
interface_var(Test{ a: true })
|
||||||
|
}
|
17
vlib/v/debug/tests/iteration.expect
Normal file
17
vlib/v/debug/tests/iteration.expect
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect "0"
|
||||||
|
expect "1"
|
||||||
|
expect "2"
|
||||||
|
expect "4"
|
||||||
|
expect "${test_file}:3 vdbg> " { send "p x\n" } timeout { exit 1 }
|
||||||
|
expect "x = 5 (int literal)" { send "c\n"} timeout { exit 1 }
|
||||||
|
expect "${test_file}:3 vdbg> " { send "p x\n" } timeout { exit 1 }
|
||||||
|
expect "x = 6 (int literal)" { send "c\n"} timeout { exit 1 }
|
||||||
|
expect "${test_file}:3 vdbg> " { send "q\n" } timeout { exit 1 }
|
||||||
|
expect "7"
|
||||||
|
expect "8"
|
||||||
|
expect "9"
|
||||||
|
expect eof
|
6
vlib/v/debug/tests/iteration.vv
Normal file
6
vlib/v/debug/tests/iteration.vv
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
for x in 0 .. 10 {
|
||||||
|
if x > 4 {
|
||||||
|
$dbg;
|
||||||
|
}
|
||||||
|
println(x)
|
||||||
|
}
|
8
vlib/v/debug/tests/mut_arg.expect
Normal file
8
vlib/v/debug/tests/mut_arg.expect
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect -ex "Break on \[main\] mut_arg in ${test_file}:10\r\n"
|
||||||
|
expect "${test_file}:10 vdbg> " { send "p b\n" } timeout { exit 1 }
|
||||||
|
expect "b = foo (&main.Test)" { send "q\n" } timeout { exit 1 }
|
||||||
|
expect eof
|
18
vlib/v/debug/tests/mut_arg.vv
Normal file
18
vlib/v/debug/tests/mut_arg.vv
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
struct Test {
|
||||||
|
a string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t &Test) str() string {
|
||||||
|
return t.a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mut(mut b Test) {
|
||||||
|
$dbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut a := Test{
|
||||||
|
a: 'foo'
|
||||||
|
}
|
||||||
|
test_mut(mut a)
|
||||||
|
}
|
9
vlib/v/debug/tests/option_unwrap.expect
Normal file
9
vlib/v/debug/tests/option_unwrap.expect
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect -ex "Break on \[main\] option_unwrap in ${test_file}:4\r\n"
|
||||||
|
expect -ex "${test_file}:4 vdbg> " { send "p a\n" } timeout { exit 1 }
|
||||||
|
expect -ex "a = Option(123) (?int)" { send "p b\n" } timeout { exit 1 }
|
||||||
|
expect -ex "b = 123 (int)" { send "q\n" } timeout { exit 1 }
|
||||||
|
expect eof
|
10
vlib/v/debug/tests/option_unwrap.vv
Normal file
10
vlib/v/debug/tests/option_unwrap.vv
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
fn option_unwrap() ? {
|
||||||
|
a := ?int(123)
|
||||||
|
b := a?
|
||||||
|
$dbg;
|
||||||
|
dump(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
option_unwrap()?
|
||||||
|
}
|
8
vlib/v/debug/tests/smartcast.expect
Normal file
8
vlib/v/debug/tests/smartcast.expect
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect -ex "Break on \[main\] smartcast in ${test_file}:3"
|
||||||
|
expect -ex "${test_file}:3 vdbg> " { send "p a\n" } timeout { exit 1 }
|
||||||
|
expect -ex "a = 1 (int)" { send "q\n" } timeout { exit 1 }
|
||||||
|
expect eof
|
9
vlib/v/debug/tests/smartcast.vv
Normal file
9
vlib/v/debug/tests/smartcast.vv
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fn smartcast(a ?int) {
|
||||||
|
if a != none {
|
||||||
|
$dbg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
smartcast(1)
|
||||||
|
}
|
10
vlib/v/debug/tests/sumtype.expect
Normal file
10
vlib/v/debug/tests/sumtype.expect
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env expect
|
||||||
|
spawn [lindex $argv 0]
|
||||||
|
set test_file [lindex $argv 1]
|
||||||
|
|
||||||
|
expect -ex "Break on \[main\] sumtype in ${test_file}:17"
|
||||||
|
expect -ex "${test_file}:17 vdbg> " { send "p a\n" } timeout { exit 1 }
|
||||||
|
expect -ex "a = Test{\r\n a: MySum(false)\r\n} (main.Test)" { send "c\n" } timeout { exit 1 }
|
||||||
|
expect -ex "${test_file}:25 vdbg> " { send "p b\n" } timeout { exit 1 }
|
||||||
|
expect -ex "b = Test{\r\n a: MySum(1)\r\n} (main.Test)" { send "q\n" } timeout { exit 1 }
|
||||||
|
expect eof
|
31
vlib/v/debug/tests/sumtype.vv
Normal file
31
vlib/v/debug/tests/sumtype.vv
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
struct Test {
|
||||||
|
a MySum
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Test2 {
|
||||||
|
a ?MySum
|
||||||
|
}
|
||||||
|
|
||||||
|
type MySum = Test | bool | int
|
||||||
|
|
||||||
|
fn sumtype() {
|
||||||
|
a := MySum(Test{
|
||||||
|
a: false
|
||||||
|
})
|
||||||
|
if a is Test {
|
||||||
|
dump(a)
|
||||||
|
$dbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
b := ?MySum(Test{
|
||||||
|
a: 1
|
||||||
|
})
|
||||||
|
if b is Test {
|
||||||
|
dump(b)
|
||||||
|
$dbg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
sumtype()
|
||||||
|
}
|
@ -513,6 +513,9 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
|
|||||||
ast.ConstDecl {
|
ast.ConstDecl {
|
||||||
f.const_decl(node)
|
f.const_decl(node)
|
||||||
}
|
}
|
||||||
|
ast.DebuggerStmt {
|
||||||
|
f.debugger_stmt(node)
|
||||||
|
}
|
||||||
ast.DeferStmt {
|
ast.DeferStmt {
|
||||||
f.defer_stmt(node)
|
f.defer_stmt(node)
|
||||||
}
|
}
|
||||||
@ -874,6 +877,10 @@ pub fn (mut f Fmt) block(node ast.Block) {
|
|||||||
f.writeln('}')
|
f.writeln('}')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut f Fmt) debugger_stmt(node ast.DebuggerStmt) {
|
||||||
|
f.writeln('\$dbg;')
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut f Fmt) branch_stmt(node ast.BranchStmt) {
|
pub fn (mut f Fmt) branch_stmt(node ast.BranchStmt) {
|
||||||
f.writeln(node.str())
|
f.writeln(node.str())
|
||||||
}
|
}
|
||||||
|
@ -240,10 +240,10 @@ mut:
|
|||||||
/////////
|
/////////
|
||||||
// out_parallel []strings.Builder
|
// out_parallel []strings.Builder
|
||||||
// out_idx int
|
// out_idx int
|
||||||
out_fn_start_pos []int // for generating multiple .c files, stores locations of all fn positions in `out` string builder
|
out_fn_start_pos []int // for generating multiple .c files, stores locations of all fn positions in `out` string builder
|
||||||
static_modifier string // for parallel_cc
|
static_modifier string // for parallel_cc
|
||||||
|
has_reflection bool // v.reflection has been imported
|
||||||
has_reflection bool
|
has_debugger bool // $dbg has been used in the code
|
||||||
reflection_strings &map[string]int
|
reflection_strings &map[string]int
|
||||||
defer_return_tmp_var string
|
defer_return_tmp_var string
|
||||||
vweb_filter_fn_name string // vweb__filter or x__vweb__filter, used by $vweb.html() for escaping strings in the templates, depending on which `vweb` import is used
|
vweb_filter_fn_name string // vweb__filter or x__vweb__filter, used by $vweb.html() for escaping strings in the templates, depending on which `vweb` import is used
|
||||||
@ -319,6 +319,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref_ &pref.Preferences) (string
|
|||||||
|| pref_.os in [.wasm32, .wasm32_emscripten])
|
|| pref_.os in [.wasm32, .wasm32_emscripten])
|
||||||
static_modifier: if pref_.parallel_cc { 'static' } else { '' }
|
static_modifier: if pref_.parallel_cc { 'static' } else { '' }
|
||||||
has_reflection: 'v.reflection' in table.modules
|
has_reflection: 'v.reflection' in table.modules
|
||||||
|
has_debugger: 'v.debug' in table.modules
|
||||||
reflection_strings: &reflection_strings
|
reflection_strings: &reflection_strings
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,6 +688,7 @@ fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) &Gen {
|
|||||||
is_cc_msvc: global_g.is_cc_msvc
|
is_cc_msvc: global_g.is_cc_msvc
|
||||||
use_segfault_handler: global_g.use_segfault_handler
|
use_segfault_handler: global_g.use_segfault_handler
|
||||||
has_reflection: 'v.reflection' in global_g.table.modules
|
has_reflection: 'v.reflection' in global_g.table.modules
|
||||||
|
has_debugger: 'v.debug' in global_g.table.modules
|
||||||
reflection_strings: global_g.reflection_strings
|
reflection_strings: global_g.reflection_strings
|
||||||
}
|
}
|
||||||
g.comptime = &comptime.ComptimeInfo{
|
g.comptime = &comptime.ComptimeInfo{
|
||||||
@ -2071,6 +2073,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||||||
ast.ComptimeFor {
|
ast.ComptimeFor {
|
||||||
g.comptime_for(node)
|
g.comptime_for(node)
|
||||||
}
|
}
|
||||||
|
ast.DebuggerStmt {
|
||||||
|
g.debugger_stmt(node)
|
||||||
|
}
|
||||||
ast.DeferStmt {
|
ast.DeferStmt {
|
||||||
mut defer_stmt := node
|
mut defer_stmt := node
|
||||||
defer_stmt.ifdef = g.defer_ifdef
|
defer_stmt.ifdef = g.defer_ifdef
|
||||||
@ -3910,6 +3915,133 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// debugger_stmt writes the call to V debugger REPL
|
||||||
|
fn (mut g Gen) debugger_stmt(node ast.DebuggerStmt) {
|
||||||
|
paline, pafile, pamod, pafn := g.panic_debug_info(node.pos)
|
||||||
|
is_anon := g.cur_fn != unsafe { nil } && g.cur_fn.is_anon
|
||||||
|
is_generic := g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0
|
||||||
|
is_method := g.cur_fn != unsafe { nil } && g.cur_fn.is_method
|
||||||
|
receiver_type := if g.cur_fn != unsafe { nil } && g.cur_fn.is_method {
|
||||||
|
g.table.type_to_str(g.cur_fn.receiver.typ)
|
||||||
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
|
scope_vars := g.file.scope.innermost(node.pos.pos).get_all_vars()
|
||||||
|
|
||||||
|
// prepares the map containing the scope variable infos
|
||||||
|
mut vars := []string{}
|
||||||
|
mut keys := strings.new_builder(100)
|
||||||
|
mut values := strings.new_builder(100)
|
||||||
|
mut count := 1
|
||||||
|
for _, obj in scope_vars {
|
||||||
|
if obj.name !in vars {
|
||||||
|
if obj is ast.Var && obj.pos.pos < node.pos.pos {
|
||||||
|
keys.write_string('_SLIT("${obj.name}")')
|
||||||
|
var_typ := if obj.smartcasts.len > 0 { obj.smartcasts.last() } else { obj.typ }
|
||||||
|
values.write_string('{.typ=_SLIT("${g.table.type_to_str(g.unwrap_generic(var_typ))}"),.value=')
|
||||||
|
obj_sym := g.table.sym(obj.typ)
|
||||||
|
cast_sym := g.table.sym(var_typ)
|
||||||
|
|
||||||
|
mut param_var := strings.new_builder(50)
|
||||||
|
if obj.smartcasts.len > 0 {
|
||||||
|
is_option_unwrap := obj.typ.has_flag(.option)
|
||||||
|
&& var_typ == obj.typ.clear_flag(.option)
|
||||||
|
is_option := obj.typ.has_flag(.option)
|
||||||
|
mut opt_cast := false
|
||||||
|
mut func := if cast_sym.info is ast.Aggregate {
|
||||||
|
''
|
||||||
|
} else {
|
||||||
|
g.get_str_fn(var_typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
param_var.write_string('(')
|
||||||
|
if obj_sym.kind == .sum_type && !obj.is_auto_heap {
|
||||||
|
if is_option {
|
||||||
|
if !is_option_unwrap {
|
||||||
|
param_var.write_string('*(')
|
||||||
|
}
|
||||||
|
styp := g.base_type(obj.typ)
|
||||||
|
param_var.write_string('*(${styp}*)')
|
||||||
|
opt_cast = true
|
||||||
|
} else {
|
||||||
|
param_var.write_string('*')
|
||||||
|
}
|
||||||
|
} else if g.table.is_interface_var(obj) || obj.ct_type_var == .smartcast {
|
||||||
|
param_var.write_string('*')
|
||||||
|
} else if is_option {
|
||||||
|
opt_cast = true
|
||||||
|
param_var.write_string('*(${g.base_type(obj.typ)}*)')
|
||||||
|
}
|
||||||
|
|
||||||
|
dot := if obj.orig_type.is_ptr() || obj.is_auto_heap { '->' } else { '.' }
|
||||||
|
if obj.ct_type_var == .smartcast {
|
||||||
|
cur_variant_sym := g.table.sym(g.unwrap_generic(g.comptime.type_map['${g.comptime.comptime_for_variant_var}.typ']))
|
||||||
|
param_var.write_string('${obj.name}${dot}_${cur_variant_sym.cname}')
|
||||||
|
} else if cast_sym.info is ast.Aggregate {
|
||||||
|
sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx])
|
||||||
|
func = g.get_str_fn(cast_sym.info.types[g.aggregate_type_idx])
|
||||||
|
param_var.write_string('${obj.name}${dot}_${sym.cname}')
|
||||||
|
} else if obj_sym.kind == .interface_ && cast_sym.kind == .interface_ {
|
||||||
|
ptr := '*'.repeat(obj.typ.nr_muls())
|
||||||
|
param_var.write_string('I_${obj_sym.cname}_as_I_${cast_sym.cname}(${ptr}${obj.name})')
|
||||||
|
} else if obj_sym.kind in [.sum_type, .interface_] {
|
||||||
|
param_var.write_string('${obj.name}')
|
||||||
|
if opt_cast {
|
||||||
|
param_var.write_string('.data)')
|
||||||
|
}
|
||||||
|
param_var.write_string('${dot}_${cast_sym.cname}')
|
||||||
|
} else if obj.typ.has_flag(.option) && !var_typ.has_flag(.option) {
|
||||||
|
param_var.write_string('${obj.name}.data')
|
||||||
|
} else {
|
||||||
|
param_var.write_string('${obj.name}')
|
||||||
|
}
|
||||||
|
param_var.write_string(')')
|
||||||
|
|
||||||
|
values.write_string('${func}(${param_var.str()})}')
|
||||||
|
} else {
|
||||||
|
func := g.get_str_fn(var_typ)
|
||||||
|
if obj.typ.has_flag(.option) && !var_typ.has_flag(.option) {
|
||||||
|
// option unwrap
|
||||||
|
base_typ := g.base_type(obj.typ)
|
||||||
|
values.write_string('${func}(*(${base_typ}*)${obj.name}.data)}')
|
||||||
|
} else {
|
||||||
|
_, str_method_expects_ptr, _ := cast_sym.str_method_info()
|
||||||
|
deref := if str_method_expects_ptr && !obj.typ.is_ptr() {
|
||||||
|
'&'
|
||||||
|
} else if obj.typ.is_ptr() && !obj.is_auto_deref {
|
||||||
|
'&'
|
||||||
|
} else if obj.typ.is_ptr() && obj.is_auto_deref {
|
||||||
|
''
|
||||||
|
} else if obj.is_auto_heap
|
||||||
|
|| (!var_typ.has_flag(.option) && var_typ.is_ptr()) {
|
||||||
|
'*'
|
||||||
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
|
values.write_string('${func}(${deref}${obj.name})}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars << obj.name
|
||||||
|
if count != scope_vars.len {
|
||||||
|
keys.write_string(',')
|
||||||
|
values.write_string(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
g.writeln('{')
|
||||||
|
g.writeln('\tMap_string_string _scope = new_map_init(&map_hash_string, &map_eq_string, &map_clone_string, &map_free_string, ${vars.len}, sizeof(string), sizeof(v__debug__DebugContextVar),')
|
||||||
|
g.write('\t\t_MOV((string[${vars.len}]){')
|
||||||
|
g.write(keys.str())
|
||||||
|
g.writeln('}),')
|
||||||
|
g.write('\t\t_MOV((v__debug__DebugContextVar[${vars.len}]){')
|
||||||
|
g.write(values.str())
|
||||||
|
g.writeln('}));')
|
||||||
|
g.writeln('\tv__debug__Debugger_interact(&g_debugger, (v__debug__DebugContextInfo){.is_anon=${is_anon},.is_generic=${is_generic},.is_method=${is_method},.receiver_typ_name=_SLIT("${receiver_type}"),.line=${paline},.file=_SLIT("${pafile}"),.mod=_SLIT("${pamod}"),.fn_name=_SLIT("${pafn}"),.scope=_scope});')
|
||||||
|
g.write('}')
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut g Gen) enum_decl(node ast.EnumDecl) {
|
fn (mut g Gen) enum_decl(node ast.EnumDecl) {
|
||||||
enum_name := util.no_dots(node.name)
|
enum_name := util.no_dots(node.name)
|
||||||
is_flag := node.is_flag
|
is_flag := node.is_flag
|
||||||
@ -5948,12 +6080,11 @@ fn (mut g Gen) write_init_function() {
|
|||||||
g.gen_reflection_data()
|
g.gen_reflection_data()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mut cleaning_up_array := []string{cap: g.table.modules.len}
|
mut cleaning_up_array := []string{cap: g.table.modules.len}
|
||||||
|
|
||||||
for mod_name in g.table.modules {
|
for mod_name in g.table.modules {
|
||||||
if g.has_reflection && mod_name == 'v.reflection' {
|
if g.has_reflection && mod_name == 'v.reflection' {
|
||||||
// ignore v.reflection already initialized above
|
// ignore v.reflection and v.debug already initialized above
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
mut const_section_header_shown := false
|
mut const_section_header_shown := false
|
||||||
|
@ -425,6 +425,7 @@ pub fn (mut f Gen) stmt(node ast.Stmt) {
|
|||||||
ast.ConstDecl {
|
ast.ConstDecl {
|
||||||
f.const_decl(node)
|
f.const_decl(node)
|
||||||
}
|
}
|
||||||
|
ast.DebuggerStmt {}
|
||||||
ast.DeferStmt {
|
ast.DeferStmt {
|
||||||
f.defer_stmt(node)
|
f.defer_stmt(node)
|
||||||
}
|
}
|
||||||
|
@ -668,6 +668,7 @@ fn (mut g JsGen) stmt_no_semi(node_ ast.Stmt) {
|
|||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
g.gen_const_decl(node)
|
g.gen_const_decl(node)
|
||||||
}
|
}
|
||||||
|
ast.DebuggerStmt {}
|
||||||
ast.DeferStmt {
|
ast.DeferStmt {
|
||||||
g.defer_stmts << node
|
g.defer_stmts << node
|
||||||
}
|
}
|
||||||
@ -775,6 +776,7 @@ fn (mut g JsGen) stmt(node_ ast.Stmt) {
|
|||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
g.gen_const_decl(node)
|
g.gen_const_decl(node)
|
||||||
}
|
}
|
||||||
|
ast.DebuggerStmt {}
|
||||||
ast.DeferStmt {
|
ast.DeferStmt {
|
||||||
g.defer_stmts << node
|
g.defer_stmts << node
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,7 @@ pub fn (mut w Walker) stmt(node_ ast.Stmt) {
|
|||||||
mut node := unsafe { node_ }
|
mut node := unsafe { node_ }
|
||||||
match mut node {
|
match mut node {
|
||||||
ast.EmptyStmt {}
|
ast.EmptyStmt {}
|
||||||
|
ast.DebuggerStmt {}
|
||||||
ast.AsmStmt {
|
ast.AsmStmt {
|
||||||
w.asm_io(node.output)
|
w.asm_io(node.output)
|
||||||
w.asm_io(node.input)
|
w.asm_io(node.input)
|
||||||
|
@ -1058,12 +1058,17 @@ fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
|||||||
return p.comptime_for()
|
return p.comptime_for()
|
||||||
}
|
}
|
||||||
.name {
|
.name {
|
||||||
mut pos := p.tok.pos()
|
// handles $dbg directly without registering token
|
||||||
expr := p.expr(0)
|
if p.peek_tok.lit == 'dbg' {
|
||||||
pos.update_last_line(p.prev_tok.line_nr)
|
return p.dbg_stmt()
|
||||||
return ast.ExprStmt{
|
} else {
|
||||||
expr: expr
|
mut pos := p.tok.pos()
|
||||||
pos: pos
|
expr := p.expr(0)
|
||||||
|
pos.update_last_line(p.prev_tok.line_nr)
|
||||||
|
return ast.ExprStmt{
|
||||||
|
expr: expr
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1149,6 +1154,16 @@ fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut p Parser) dbg_stmt() ast.DebuggerStmt {
|
||||||
|
pos := p.tok.pos()
|
||||||
|
p.check(.dollar)
|
||||||
|
p.check(.name)
|
||||||
|
p.register_auto_import('v.debug')
|
||||||
|
return ast.DebuggerStmt{
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut p Parser) semicolon_stmt() ast.SemicolonStmt {
|
fn (mut p Parser) semicolon_stmt() ast.SemicolonStmt {
|
||||||
pos := p.tok.pos()
|
pos := p.tok.pos()
|
||||||
p.check(.semicolon)
|
p.check(.semicolon)
|
||||||
|
@ -178,6 +178,7 @@ pub fn (mut t Transformer) stmt(mut node ast.Stmt) ast.Stmt {
|
|||||||
ast.EmptyStmt {}
|
ast.EmptyStmt {}
|
||||||
ast.NodeError {}
|
ast.NodeError {}
|
||||||
ast.AsmStmt {}
|
ast.AsmStmt {}
|
||||||
|
ast.DebuggerStmt {}
|
||||||
ast.AssertStmt {
|
ast.AssertStmt {
|
||||||
return t.assert_stmt(mut node)
|
return t.assert_stmt(mut node)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user