compiler: streamline main function handling
* compiler: streamline C main function generation * fix most tests * compiler: fix for 'go update()' in graph.v . More precise parser error messages. * Fix temporarily examples/hot_reload/message.v by using os inside it (os.clear). * Make graph.v easier to quickly modify by defining y outside the loop. * Fix failure of /v/nv/compiler/tests/defer_test.v when run with 'v -g' (#line directive was not on its own line, but right after } ). * Do not pass the os.args to tests, even if the tests import os (they are more stable when run in a controlled environment). * fix declared and not used in the js backend. * fix js main => main__main too.pull/2159/head
parent
0160c7a89d
commit
a4cbe78d97
|
@ -63,7 +63,7 @@ fn (g mut CGen) genln(s string) {
|
|||
g.cur_line = '$g.cur_line $s'
|
||||
if g.cur_line != '' {
|
||||
if g.line_directives && g.cur_line.trim_space() != '' {
|
||||
g.lines << '#line $g.line "$g.file"'
|
||||
g.lines << '\n#line $g.line "$g.file"'
|
||||
}
|
||||
g.lines << g.cur_line
|
||||
g.prev_line = g.cur_line
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
module main
|
||||
|
||||
import(
|
||||
os
|
||||
strings
|
||||
)
|
||||
|
||||
|
@ -34,6 +33,7 @@ mut:
|
|||
is_decl bool // type myfn fn(int, int)
|
||||
defer_text []string
|
||||
//gen_types []string
|
||||
fn_name_sp ScannerPos
|
||||
}
|
||||
|
||||
fn (p &Parser) find_var(name string) ?Var {
|
||||
|
@ -215,6 +215,7 @@ fn (p mut Parser) fn_decl() {
|
|||
else {
|
||||
f.name = p.check_name()
|
||||
}
|
||||
f.fn_name_sp = p.scanner.get_scanner_pos()
|
||||
// C function header def? (fn C.NSMakeRect(int,int,int,int))
|
||||
is_c := f.name == 'C' && p.tok == .dot
|
||||
// Just fn signature? only builtin.v + default build mode
|
||||
|
@ -244,7 +245,7 @@ fn (p mut Parser) fn_decl() {
|
|||
}
|
||||
// full mod function name
|
||||
// os.exit ==> os__exit()
|
||||
if !is_c && !p.builtin_mod && p.mod != 'main' && receiver_typ.len == 0 {
|
||||
if !is_c && !p.builtin_mod && receiver_typ.len == 0 {
|
||||
f.name = p.prepend_mod(f.name)
|
||||
}
|
||||
if p.first_pass() && receiver_typ.len == 0 {
|
||||
|
@ -318,14 +319,12 @@ fn (p mut Parser) fn_decl() {
|
|||
}
|
||||
// Register function
|
||||
f.typ = typ
|
||||
mut str_args := f.str_args(p.table)
|
||||
str_args := f.str_args(p.table)
|
||||
// Special case for main() args
|
||||
if f.name == 'main' && !has_receiver {
|
||||
if f.name == 'main__main' && !has_receiver {
|
||||
if str_args != '' || typ != 'void' {
|
||||
p.error('fn main must have no arguments and no return values')
|
||||
p.error_with_position('fn main must have no arguments and no return values', f.fn_name_sp)
|
||||
}
|
||||
typ = 'int'
|
||||
str_args = 'int argc, char** argv'
|
||||
}
|
||||
dll_export_linkage := if p.os == .msvc && p.attr == 'live' && p.pref.is_so {
|
||||
'__declspec(dllexport) '
|
||||
|
@ -341,7 +340,7 @@ fn (p mut Parser) fn_decl() {
|
|||
// Internally it's still stored as "register" in type User
|
||||
mut fn_name_cgen := p.table.fn_gen_name(f)
|
||||
// Start generation of the function body
|
||||
skip_main_in_test := f.name == 'main' && p.pref.is_test
|
||||
skip_main_in_test := false
|
||||
if !is_c && !is_live && !is_sig && !is_fn_header && !skip_main_in_test {
|
||||
if p.pref.obfuscate {
|
||||
p.genln('; // $f.name')
|
||||
|
@ -434,7 +433,7 @@ fn (p mut Parser) fn_decl() {
|
|||
fn_decl += '; // $f.name'
|
||||
}
|
||||
// Add function definition to the top
|
||||
if !is_c && f.name != 'main' && p.first_pass() {
|
||||
if !is_c && p.first_pass() {
|
||||
// TODO hack to make Volt compile without -embed_vlib
|
||||
if f.name == 'darwin__nsstring' && p.pref.build_mode == .default_mode {
|
||||
return
|
||||
|
@ -447,37 +446,10 @@ fn (p mut Parser) fn_decl() {
|
|||
//p.genln('// live_function body start')
|
||||
p.genln('pthread_mutex_lock(&live_fn_mutex);')
|
||||
}
|
||||
if f.name == 'main' || f.name == 'WinMain' {
|
||||
p.genln('init_consts();')
|
||||
if 'os' in p.table.imports {
|
||||
if f.name == 'main' {
|
||||
p.genln('os__args = os__init_os_args(argc, (byteptr*)argv);')
|
||||
}
|
||||
else if f.name == 'WinMain' {
|
||||
p.genln('os__args = os__parse_windows_cmd_line(pCmdLine);')
|
||||
}
|
||||
}
|
||||
// We are in live code reload mode, call the .so loader in bg
|
||||
if p.pref.is_live {
|
||||
file_base := os.filename(p.file_path).replace('.v', '')
|
||||
if p.os != .windows && p.os != .msvc {
|
||||
so_name := file_base + '.so'
|
||||
p.genln('
|
||||
load_so("$so_name");
|
||||
pthread_t _thread_so;
|
||||
pthread_create(&_thread_so , NULL, &reload_so, NULL); ')
|
||||
} else {
|
||||
so_name := file_base + if p.os == .msvc {'.dll'} else {'.so'}
|
||||
p.genln('
|
||||
live_fn_mutex = CreateMutexA(0, 0, 0);
|
||||
load_so("$so_name");
|
||||
unsigned long _thread_so;
|
||||
_thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
||||
')
|
||||
}
|
||||
}
|
||||
|
||||
if f.name == 'main__main' || f.name == 'main' || f.name == 'WinMain' {
|
||||
if p.pref.is_test && !p.scanner.file_path.contains('/volt') {
|
||||
p.error('tests cannot have function `main`')
|
||||
p.error_with_position('tests cannot have function `main`', f.fn_name_sp)
|
||||
}
|
||||
}
|
||||
// println('is_c=$is_c name=$f.name')
|
||||
|
@ -486,7 +458,7 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
|||
return
|
||||
}
|
||||
// Profiling mode? Start counting at the beginning of the function (save current time).
|
||||
if p.pref.is_prof && f.name != 'main' && f.name != 'time__ticks' {
|
||||
if p.pref.is_prof && f.name != 'time__ticks' {
|
||||
p.genln('double _PROF_START = time__ticks();//$f.name')
|
||||
cgen_name := p.table.fn_gen_name(f)
|
||||
if f.defer_text.len > f.scope_level {
|
||||
|
@ -509,8 +481,8 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);
|
|||
p.genln(f.defer_text[f.scope_level])
|
||||
}
|
||||
}
|
||||
if typ != 'void' && !p.returns && f.name != 'main' && f.name != 'WinMain' {
|
||||
p.error('$f.name must return "$typ"')
|
||||
if typ != 'void' && !p.returns {
|
||||
p.error_with_position('$f.name must return "$typ"', f.fn_name_sp)
|
||||
}
|
||||
if p.attr == 'live' && p.pref.is_so {
|
||||
//p.genln('// live_function body end')
|
||||
|
|
|
@ -36,7 +36,7 @@ fn (p mut Parser) gen_fn_decl(f Fn, typ, _str_args string) {
|
|||
fn (p mut Parser) gen_blank_identifier_assign() {
|
||||
p.check_name()
|
||||
p.check_space(.assign)
|
||||
typ := p.bool_expression()
|
||||
p.bool_expression()
|
||||
or_else := p.tok == .key_orelse
|
||||
//tmp := p.get_tmp()
|
||||
if or_else {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
module main
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
fn (v &V) generate_hotcode_reloading_compiler_flags() []string {
|
||||
mut a := []string
|
||||
mut a := []string
|
||||
if v.pref.is_live || v.pref.is_so {
|
||||
// See 'man dlopen', and test running a GUI program compiled with -live
|
||||
if (v.os == .linux || os.user_os() == 'linux'){
|
||||
|
@ -13,11 +14,11 @@ fn (v &V) generate_hotcode_reloading_compiler_flags() []string {
|
|||
a << '-flat_namespace'
|
||||
}
|
||||
}
|
||||
return a
|
||||
return a
|
||||
}
|
||||
|
||||
fn (v &V) generate_hotcode_reloading_declarations() {
|
||||
mut cgen := v.cgen
|
||||
mut cgen := v.cgen
|
||||
if v.os != .windows && v.os != .msvc {
|
||||
if v.pref.is_so {
|
||||
cgen.genln('pthread_mutex_t live_fn_mutex;')
|
||||
|
@ -35,9 +36,33 @@ fn (v &V) generate_hotcode_reloading_declarations() {
|
|||
}
|
||||
}
|
||||
|
||||
fn (v &V) generate_hot_reload_code() {
|
||||
mut cgen := v.cgen
|
||||
fn (v &V) generate_hotcode_reloading_main_caller() {
|
||||
if !v.pref.is_live { return }
|
||||
// We are in live code reload mode, so start the .so loader in the background
|
||||
mut cgen := v.cgen
|
||||
cgen.genln('')
|
||||
file_base := os.filename(v.dir).replace('.v', '')
|
||||
if !(v.os == .windows || v.os == .msvc) {
|
||||
// unix:
|
||||
so_name := file_base + '.so'
|
||||
cgen.genln(' char *live_library_name = "$so_name";')
|
||||
cgen.genln(' load_so(live_library_name);')
|
||||
cgen.genln(' pthread_t _thread_so;')
|
||||
cgen.genln(' pthread_create(&_thread_so , NULL, &reload_so, live_library_name);')
|
||||
} else {
|
||||
// windows:
|
||||
so_name := file_base + if v.os == .msvc {'.dll'} else {'.so'}
|
||||
cgen.genln(' char *live_library_name = "$so_name";')
|
||||
cgen.genln(' live_fn_mutex = CreateMutexA(0, 0, 0);')
|
||||
cgen.genln(' load_so(live_library_name);')
|
||||
cgen.genln(' unsigned long _thread_so;')
|
||||
cgen.genln(' _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);')
|
||||
}
|
||||
}
|
||||
|
||||
fn (v &V) generate_hot_reload_code() {
|
||||
mut cgen := v.cgen
|
||||
|
||||
// Hot code reloading
|
||||
if v.pref.is_live {
|
||||
mut file := os.realpath(v.dir)
|
||||
|
@ -46,24 +71,33 @@ fn (v &V) generate_hot_reload_code() {
|
|||
// Need to build .so file before building the live application
|
||||
// The live app needs to load this .so file on initialization.
|
||||
mut vexe := os.args[0]
|
||||
|
||||
|
||||
if os.user_os() == 'windows' {
|
||||
vexe = vexe.replace('\\', '\\\\')
|
||||
file = file.replace('\\', '\\\\')
|
||||
}
|
||||
|
||||
|
||||
mut msvc := ''
|
||||
if v.os == .msvc {
|
||||
msvc = '-os msvc'
|
||||
}
|
||||
|
||||
|
||||
mut debug := ''
|
||||
|
||||
|
||||
if v.pref.is_debug {
|
||||
debug = '-debug'
|
||||
}
|
||||
|
||||
os.system('$vexe $msvc $debug -o $file_base -shared $file')
|
||||
|
||||
cmd_compile_shared_library := '$vexe $msvc $debug -o $file_base -shared $file'
|
||||
if v.pref.show_c_cmd {
|
||||
println(cmd_compile_shared_library)
|
||||
}
|
||||
ticks := time.ticks()
|
||||
os.system(cmd_compile_shared_library)
|
||||
diff := time.ticks() - ticks
|
||||
println('compiling shared library took $diff ms')
|
||||
println('=========\n')
|
||||
|
||||
cgen.genln('
|
||||
|
||||
void lfnmutex_print(char *s){
|
||||
|
|
|
@ -368,7 +368,7 @@ fn (v mut V) compile() {
|
|||
}
|
||||
}
|
||||
$if js {
|
||||
cgen.genln('main();')
|
||||
cgen.genln('main__main();')
|
||||
}
|
||||
cgen.save()
|
||||
v.cc()
|
||||
|
@ -439,35 +439,53 @@ string _STR_TMP(const char *fmt, ...) {
|
|||
// It can be skipped in single file programs
|
||||
if v.pref.is_script {
|
||||
//println('Generating main()...')
|
||||
cgen.genln('int main() { init_consts();')
|
||||
v.gen_main_start(true)
|
||||
cgen.genln('$cgen.fn_main;')
|
||||
cgen.genln('return 0; }')
|
||||
v.gen_main_end('return 0')
|
||||
}
|
||||
else {
|
||||
println('panic: function `main` is undeclared in the main module')
|
||||
exit(1)
|
||||
verror('function `main` is not declared in the main module')
|
||||
}
|
||||
}
|
||||
else if v.pref.is_test {
|
||||
if v.table.main_exists() {
|
||||
verror('test files cannot have function `main`')
|
||||
}
|
||||
// make sure there's at least on test function
|
||||
}
|
||||
if !v.table.has_at_least_one_test_fn() {
|
||||
verror('test files need to have at least one test function')
|
||||
}
|
||||
// Generate `main` which calls every single test function
|
||||
cgen.genln('int main() { init_consts();')
|
||||
}
|
||||
// Generate a C `main`, which calls every single test function
|
||||
v.gen_main_start(false)
|
||||
for _, f in v.table.fns {
|
||||
if f.name.starts_with('test_') {
|
||||
if f.name.starts_with('main__test_') {
|
||||
cgen.genln('$f.name();')
|
||||
}
|
||||
}
|
||||
cgen.genln('return g_test_ok == 0; }')
|
||||
v.gen_main_end('return g_test_ok == 0')
|
||||
}
|
||||
else if v.table.main_exists() {
|
||||
v.gen_main_start(true)
|
||||
cgen.genln(' main__main();')
|
||||
v.gen_main_end('return 0')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (v mut V) gen_main_start(add_os_args bool){
|
||||
v.cgen.genln('int main(int argc, char** argv) { ')
|
||||
v.cgen.genln(' init_consts();')
|
||||
if add_os_args && 'os' in v.table.imports {
|
||||
v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);')
|
||||
}
|
||||
v.generate_hotcode_reloading_main_caller()
|
||||
v.cgen.genln('')
|
||||
}
|
||||
fn (v mut V) gen_main_end(return_statement string){
|
||||
v.cgen.genln('')
|
||||
v.cgen.genln(' $return_statement;')
|
||||
v.cgen.genln('}')
|
||||
}
|
||||
|
||||
fn final_target_out_name(out_name string) string {
|
||||
mut cmd := if out_name.starts_with('/') {
|
||||
out_name
|
||||
|
|
|
@ -2037,7 +2037,10 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
|
|||
// field
|
||||
if has_field {
|
||||
struct_field := if typ.name != 'Option' { p.table.var_cgen_name(field_name) } else { field_name }
|
||||
field := p.table.find_field(typ, struct_field) or { panic('field') }
|
||||
field := p.table.find_field(typ, struct_field) or {
|
||||
p.error('missing field: $struct_field in type $typ.name')
|
||||
exit(1)
|
||||
}
|
||||
if !field.is_mut && !p.has_immutable_field {
|
||||
p.has_immutable_field = true
|
||||
p.first_immutable_field = field
|
||||
|
@ -3008,7 +3011,10 @@ fn (p mut Parser) struct_init(typ string) string {
|
|||
if field in inited_fields {
|
||||
p.error('already initialized field `$field` in `$t.name`')
|
||||
}
|
||||
f := t.find_field(field) or { panic('field') }
|
||||
f := t.find_field(field) or {
|
||||
p.error('no such field: "$field" in type $typ')
|
||||
break
|
||||
}
|
||||
inited_fields << field
|
||||
p.gen_struct_field_init(field)
|
||||
p.check(.colon)
|
||||
|
@ -3774,23 +3780,35 @@ fn (p &Parser) prepend_mod(name string) string {
|
|||
|
||||
fn (p mut Parser) go_statement() {
|
||||
p.check(.key_go)
|
||||
mut gopos := p.scanner.get_scanner_pos()
|
||||
// TODO copypasta of name_expr() ?
|
||||
// Method
|
||||
if p.peek() == .dot {
|
||||
// Method
|
||||
var_name := p.lit
|
||||
v := p.find_var(var_name) or { return }
|
||||
v := p.find_var(var_name) or {
|
||||
return
|
||||
}
|
||||
p.mark_var_used(v)
|
||||
gopos = p.scanner.get_scanner_pos()
|
||||
p.next()
|
||||
p.check(.dot)
|
||||
typ := p.table.find_type(v.typ)
|
||||
method := p.table.find_method(typ, p.lit) or { panic('go method') }
|
||||
method := p.table.find_method(typ, p.lit) or {
|
||||
p.error_with_position('go method missing $var_name', gopos)
|
||||
return
|
||||
}
|
||||
p.async_fn_call(method, 0, var_name, v.typ)
|
||||
}
|
||||
// Normal function
|
||||
else {
|
||||
f := p.table.find_fn(p.lit) or { panic('fn') }
|
||||
f_name := p.lit
|
||||
// Normal function
|
||||
f := p.table.find_fn(p.prepend_mod(f_name)) or {
|
||||
println( p.table.debug_fns() )
|
||||
p.error_with_position('can not find function $f_name', gopos)
|
||||
return
|
||||
}
|
||||
if f.name == 'println' || f.name == 'print' {
|
||||
p.error('`go` cannot be used with `println`')
|
||||
p.error_with_position('`go` cannot be used with `println`', gopos)
|
||||
}
|
||||
p.async_fn_call(f, 0, '', '')
|
||||
}
|
||||
|
|
|
@ -645,6 +645,17 @@ fn (s &Scanner) error_with_col(msg string, col int) {
|
|||
column := col-1
|
||||
linestart := s.find_current_line_start_position()
|
||||
lineend := s.find_current_line_end_position()
|
||||
|
||||
fullpath := os.realpath( s.file_path )
|
||||
// The filepath:line:col: format is the default C compiler
|
||||
// error output format. It allows editors and IDE's like
|
||||
// emacs to quickly find the errors in the output
|
||||
// and jump to their source with a keyboard shortcut.
|
||||
// Using only the filename leads to inability of IDE/editors
|
||||
// to find the source file, when it is in another folder.
|
||||
//println('${s.file_path}:${s.line_nr + 1}:${column+1}: $msg')
|
||||
println('${fullpath}:${s.line_nr + 1}:${column+1}: $msg')
|
||||
|
||||
if s.should_print_line_on_error && lineend > linestart {
|
||||
line := s.text.substr( linestart, lineend )
|
||||
// The pointerline should have the same spaces/tabs as the offending
|
||||
|
@ -661,16 +672,7 @@ fn (s &Scanner) error_with_col(msg string, col int) {
|
|||
println(line)
|
||||
println(pointerline)
|
||||
}
|
||||
fullpath := os.realpath( s.file_path )
|
||||
_ = fullpath
|
||||
// The filepath:line:col: format is the default C compiler
|
||||
// error output format. It allows editors and IDE's like
|
||||
// emacs to quickly find the errors in the output
|
||||
// and jump to their source with a keyboard shortcut.
|
||||
// Using only the filename leads to inability of IDE/editors
|
||||
// to find the source file, when it is in another folder.
|
||||
//println('${s.file_path}:${s.line_nr + 1}:${column+1}: $msg')
|
||||
println('${fullpath}:${s.line_nr + 1}:${column+1}: $msg')
|
||||
|
||||
exit(1)
|
||||
}
|
||||
|
||||
|
|
|
@ -698,7 +698,7 @@ fn (table &Table) is_interface(name string) bool {
|
|||
// Do we have fn main()?
|
||||
fn (t &Table) main_exists() bool {
|
||||
for _, f in t.fns {
|
||||
if f.name == 'main' {
|
||||
if f.name == 'main__main' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -707,7 +707,7 @@ fn (t &Table) main_exists() bool {
|
|||
|
||||
fn (t &Table) has_at_least_one_test_fn() bool {
|
||||
for _, f in t.fns {
|
||||
if f.name.starts_with('test_') {
|
||||
if f.name.starts_with('main__test_') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import gx
|
|||
import gg
|
||||
import time
|
||||
import glfw
|
||||
// import math
|
||||
import math
|
||||
import os
|
||||
|
||||
const (
|
||||
Size = 700
|
||||
|
@ -16,6 +17,7 @@ struct Context {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
os.clear()
|
||||
glfw.init()
|
||||
ctx:= &Context{
|
||||
gg: gg.new_context(gg.Cfg {
|
||||
|
@ -40,11 +42,12 @@ fn main() {
|
|||
fn (ctx &Context) draw() {
|
||||
ctx.gg.draw_line(0, Size / 2, Size, Size / 2) // x axis
|
||||
ctx.gg.draw_line(Size / 2, 0, Size / 2, Size) // y axis
|
||||
center := f64(Size / 2)
|
||||
center := f64(Size / 2)
|
||||
mut y := 0.0
|
||||
for x := -10.0; x <= 10.0; x += 0.002 {
|
||||
y := x * x - 1
|
||||
//y := (x + 3) * (x + 3) - 1
|
||||
//y := math.sqrt(30.0 - x * x)
|
||||
y = x * x - 1
|
||||
//y = (x + 3) * (x + 3) - 1
|
||||
//y = math.sqrt(30.0 - x * x)
|
||||
ctx.gg.draw_rect(center + x * Scale, center - y * Scale, 1, 1, gx.Black)
|
||||
//ctx.gg.draw_rect(center + x * Scale, center + y * Scale, 1, 1, gx.Black)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// v -live message.v
|
||||
module main
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
[live]
|
||||
|
@ -10,6 +11,7 @@ fn print_message() {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
os.clear()
|
||||
for {
|
||||
print_message()
|
||||
time.sleep_ms(500)
|
||||
|
|
Loading…
Reference in New Issue