diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index fc18f0cb5c..560d606539 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -9,6 +9,9 @@ import v.token pub fn (mut p Parser) expr(precedence int) ast.Expr { return p.check_expr(precedence) or { + if token.is_decl(p.tok.kind) && p.disallow_declarations_in_script_mode() { + return ast.empty_expr() + } p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.pos()) } } diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 8f706aab9f..461e8fc4bf 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -673,10 +673,10 @@ fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInf fn (mut p Parser) anon_fn() ast.AnonFn { pos := p.tok.pos() p.check(.key_fn) - if p.pref.is_script && p.tok.kind == .name { - p.error_with_pos('function declarations in script mode should be before all script statements', - p.tok.pos()) - return ast.AnonFn{} + if p.tok.kind == .name { + if p.disallow_declarations_in_script_mode() { + return ast.AnonFn{} + } } old_inside_defer := p.inside_defer p.inside_defer = false diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 0656b53624..7e022cab4d 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -93,6 +93,8 @@ mut: codegen_text string struct_init_generic_types []ast.Type if_cond_comments []ast.Comment + script_mode bool + script_mode_start_token token.Token } __global codegen_files = []&ast.File{} @@ -685,12 +687,17 @@ pub fn (mut p Parser) top_stmt() ast.Stmt { else { p.inside_fn = true if p.pref.is_script && !p.pref.is_test { + p.script_mode = true + p.script_mode_start_token = p.tok + p.open_scope() mut stmts := []ast.Stmt{} for p.tok.kind != .eof { stmts << p.stmt(false) } p.close_scope() + + p.script_mode = false return ast.FnDecl{ name: 'main.main' short_name: 'main' @@ -3276,6 +3283,9 @@ fn (mut p Parser) const_decl() ast.ConstDecl { p.next() } const_pos := p.tok.pos() + if p.disallow_declarations_in_script_mode() { + return ast.ConstDecl{} + } p.check(.key_const) is_block := p.tok.kind == .lpar if is_block { @@ -3390,6 +3400,9 @@ fn (mut p Parser) global_decl() ast.GlobalDecl { } start_pos := p.tok.pos() p.check(.key_global) + if p.disallow_declarations_in_script_mode() { + return ast.GlobalDecl{} + } is_block := p.tok.kind == .lpar if is_block { p.next() // ( @@ -3485,6 +3498,9 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { } p.check(.key_enum) end_pos := p.tok.pos() + if p.disallow_declarations_in_script_mode() { + return ast.EnumDecl{} + } enum_name := p.check_name() if enum_name.len == 1 { p.error_with_pos('single letter capital names are reserved for generic template types.', @@ -3597,6 +3613,9 @@ fn (mut p Parser) type_decl() ast.TypeDecl { end_pos := p.tok.pos() decl_pos := start_pos.extend(end_pos) name_pos := p.tok.pos() + if p.disallow_declarations_in_script_mode() { + return ast.SumTypeDecl{} + } name := p.check_name() if name.len == 1 && name[0].is_capital() { p.error_with_pos('single letter capital names are reserved for generic template types.', @@ -3889,6 +3908,15 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt { } } +fn (mut p Parser) disallow_declarations_in_script_mode() bool { + if p.script_mode { + p.note_with_pos('script mode started here', p.script_mode_start_token.pos()) + p.error_with_pos('all definitions must occur before code in script mode', p.tok.pos()) + return true + } + return false +} + fn (mut p Parser) trace(fbase string, message string) { if p.file_base == fbase { println('> p.trace | ${fbase:-10s} | $message') diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index fab4a54ffd..6502be94f6 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -36,6 +36,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl { } name_pos := p.tok.pos() p.check_for_impure_v(language, name_pos) + if p.disallow_declarations_in_script_mode() { + return ast.StructDecl{} + } mut name := p.check_name() // defer { // if name.contains('App') { @@ -471,6 +474,9 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { } name_pos := p.tok.pos() p.check_for_impure_v(language, name_pos) + if p.disallow_declarations_in_script_mode() { + return ast.InterfaceDecl{} + } modless_name := p.check_name() if modless_name == 'IError' && p.mod != 'builtin' { p.error_with_pos('cannot register interface `IError`, it is builtin interface type', diff --git a/vlib/v/parser/tests/invalid_enum_decl_script_err.out b/vlib/v/parser/tests/invalid_enum_decl_script_err.out new file mode 100644 index 0000000000..d8145baa02 --- /dev/null +++ b/vlib/v/parser/tests/invalid_enum_decl_script_err.out @@ -0,0 +1,12 @@ +vlib/v/parser/tests/invalid_enum_decl_script_err.vv:1:1: notice: script mode started here + 1 | mynum := 10 + | ~~~~~ + 2 | + 3 | enum MyEnum { +vlib/v/parser/tests/invalid_enum_decl_script_err.vv:3:1: error: all definitions must occur before code in script mode + 1 | mynum := 10 + 2 | + 3 | enum MyEnum { + | ~~~~ + 4 | aa + 5 | bb diff --git a/vlib/v/parser/tests/invalid_enum_decl_script_err.vv b/vlib/v/parser/tests/invalid_enum_decl_script_err.vv new file mode 100644 index 0000000000..c693e57a40 --- /dev/null +++ b/vlib/v/parser/tests/invalid_enum_decl_script_err.vv @@ -0,0 +1,7 @@ +mynum := 10 + +enum MyEnum { + aa + bb + cc +} diff --git a/vlib/v/parser/tests/invalid_fn_decl_script_err.out b/vlib/v/parser/tests/invalid_fn_decl_script_err.out index aead2d093f..949df60548 100644 --- a/vlib/v/parser/tests/invalid_fn_decl_script_err.out +++ b/vlib/v/parser/tests/invalid_fn_decl_script_err.out @@ -1,7 +1,12 @@ -vlib/v/parser/tests/invalid_fn_decl_script_err.vv:3:4: error: function declarations in script mode should be before all script statements +vlib/v/parser/tests/invalid_fn_decl_script_err.vv:1:1: notice: script mode started here 1 | mynum := 10 - 2 | - 3 | fn main() { - | ~~~~ + | ~~~~~ + 2 | + 3 | fn my_function() { +vlib/v/parser/tests/invalid_fn_decl_script_err.vv:3:4: error: all definitions must occur before code in script mode + 1 | mynum := 10 + 2 | + 3 | fn my_function() { + | ~~~~~~~~~~~ 4 | println(mynum) - 5 | } \ No newline at end of file + 5 | } diff --git a/vlib/v/parser/tests/invalid_fn_decl_script_err.vv b/vlib/v/parser/tests/invalid_fn_decl_script_err.vv index 196895a1cf..b724d16bbb 100644 --- a/vlib/v/parser/tests/invalid_fn_decl_script_err.vv +++ b/vlib/v/parser/tests/invalid_fn_decl_script_err.vv @@ -1,5 +1,5 @@ mynum := 10 -fn main() { +fn my_function() { println(mynum) } diff --git a/vlib/v/parser/tests/invalid_interface_decl_script_err.out b/vlib/v/parser/tests/invalid_interface_decl_script_err.out new file mode 100644 index 0000000000..2072b23e1b --- /dev/null +++ b/vlib/v/parser/tests/invalid_interface_decl_script_err.out @@ -0,0 +1,11 @@ +vlib/v/parser/tests/invalid_interface_decl_script_err.vv:1:1: notice: script mode started here + 1 | mynum := 10 + | ~~~~~ + 2 | + 3 | interface IUniversal { +vlib/v/parser/tests/invalid_interface_decl_script_err.vv:3:1: error: all definitions must occur before code in script mode + 1 | mynum := 10 + 2 | + 3 | interface IUniversal { + | ~~~~~~~~~ + 4 | } diff --git a/vlib/v/parser/tests/invalid_interface_decl_script_err.vv b/vlib/v/parser/tests/invalid_interface_decl_script_err.vv new file mode 100644 index 0000000000..26ca7ce589 --- /dev/null +++ b/vlib/v/parser/tests/invalid_interface_decl_script_err.vv @@ -0,0 +1,4 @@ +mynum := 10 + +interface IUniversal { +} diff --git a/vlib/v/parser/tests/invalid_struct_decl_script_err.out b/vlib/v/parser/tests/invalid_struct_decl_script_err.out new file mode 100644 index 0000000000..ed5ebea055 --- /dev/null +++ b/vlib/v/parser/tests/invalid_struct_decl_script_err.out @@ -0,0 +1,12 @@ +vlib/v/parser/tests/invalid_struct_decl_script_err.vv:1:1: notice: script mode started here + 1 | mynum := 10 + | ~~~~~ + 2 | + 3 | struct Abc { +vlib/v/parser/tests/invalid_struct_decl_script_err.vv:3:1: error: all definitions must occur before code in script mode + 1 | mynum := 10 + 2 | + 3 | struct Abc { + | ~~~~~~ + 4 | x int + 5 | } diff --git a/vlib/v/parser/tests/invalid_struct_decl_script_err.vv b/vlib/v/parser/tests/invalid_struct_decl_script_err.vv new file mode 100644 index 0000000000..1bf1eccb3c --- /dev/null +++ b/vlib/v/parser/tests/invalid_struct_decl_script_err.vv @@ -0,0 +1,5 @@ +mynum := 10 + +struct Abc { + x int +}