parser: make script mode errors more informative, when a top level declaration is encountered, after script mode had already started
							parent
							
								
									20139ad756
								
							
						
					
					
						commit
						668d1b04d2
					
				| 
						 | 
				
			
			@ -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())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
mynum := 10
 | 
			
		||||
 | 
			
		||||
enum MyEnum {
 | 
			
		||||
	aa
 | 
			
		||||
	bb
 | 
			
		||||
	cc
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 | }
 | 
			
		||||
    5 | }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
mynum := 10
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
fn my_function() {
 | 
			
		||||
	println(mynum)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 | }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
mynum := 10
 | 
			
		||||
 | 
			
		||||
interface IUniversal {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 | }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
mynum := 10
 | 
			
		||||
 | 
			
		||||
struct Abc {
 | 
			
		||||
	x int
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue