compiler: multiple return - allow mut decl & disallow assignment with deceleration (#2251)
							parent
							
								
									ac5241b5bd
								
							
						
					
					
						commit
						8fea5170be
					
				| 
						 | 
				
			
			@ -1426,27 +1426,31 @@ fn (p mut Parser) var_decl() {
 | 
			
		|||
		p.fspace()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mut names := []string
 | 
			
		||||
	mut vtoken_idxs := []int
 | 
			
		||||
	mut var_token_idxs := [p.cur_tok_index()]
 | 
			
		||||
	mut var_mut := [is_mut] // add first var mut
 | 
			
		||||
	mut var_names := [p.check_name()] // add first variable
 | 
			
		||||
	
 | 
			
		||||
	vtoken_idxs << p.cur_tok_index()
 | 
			
		||||
	// first variable
 | 
			
		||||
	names << p.check_name()
 | 
			
		||||
	p.scanner.validate_var_name(names[0])
 | 
			
		||||
	mut new_vars := 0
 | 
			
		||||
	if names[0] != '_' && !p.known_var(names[0]) {
 | 
			
		||||
		new_vars++
 | 
			
		||||
	}
 | 
			
		||||
	p.scanner.validate_var_name(var_names[0])
 | 
			
		||||
	// mut new_vars := 0
 | 
			
		||||
	// if var_names[0] != '_' && !p.known_var(var_names[0]) {
 | 
			
		||||
	// 	new_vars++
 | 
			
		||||
	// }
 | 
			
		||||
	// more than 1 vars (multiple returns)
 | 
			
		||||
	for p.tok == .comma {
 | 
			
		||||
		p.check(.comma)
 | 
			
		||||
		vtoken_idxs << p.cur_tok_index()
 | 
			
		||||
		name := p.check_name()
 | 
			
		||||
		names << name
 | 
			
		||||
		p.scanner.validate_var_name(name)
 | 
			
		||||
		if name != '_' && !p.known_var(name) {
 | 
			
		||||
			new_vars++
 | 
			
		||||
		if p.tok == .key_mut {
 | 
			
		||||
			p.check(.key_mut)
 | 
			
		||||
			var_mut << true
 | 
			
		||||
		} else {
 | 
			
		||||
			var_mut << false
 | 
			
		||||
		}
 | 
			
		||||
		var_token_idxs << p.cur_tok_index()
 | 
			
		||||
		var_name := p.check_name()
 | 
			
		||||
		p.scanner.validate_var_name(var_name)
 | 
			
		||||
		// if var_name != '_' && !p.known_var(var_name) {
 | 
			
		||||
		// 	new_vars++
 | 
			
		||||
		// }
 | 
			
		||||
		var_names << var_name
 | 
			
		||||
	}
 | 
			
		||||
	is_assign := p.tok == .assign
 | 
			
		||||
	is_decl_assign := p.tok == .decl_assign
 | 
			
		||||
| 
						 | 
				
			
			@ -1458,72 +1462,78 @@ fn (p mut Parser) var_decl() {
 | 
			
		|||
		p.error('expected `=` or `:=`')
 | 
			
		||||
	}
 | 
			
		||||
	// all vars on left of `:=` already defined
 | 
			
		||||
	if is_decl_assign && names.len > 1 && new_vars == 0 {
 | 
			
		||||
		p.error('no new variables on left side of `:=`')
 | 
			
		||||
	}
 | 
			
		||||
	p.var_decl_name = if names.len > 1 { '__ret_'+names.join('_') } else { names[0] }
 | 
			
		||||
	// if is_decl_assign && var_names.len > 1 && new_vars == 0 {
 | 
			
		||||
	// 	p.error_with_token_index('no new variables on left side of `:=`', var_token_idxs.last())
 | 
			
		||||
	// }
 | 
			
		||||
	p.var_decl_name = if var_names.len > 1 { '_V_mret_'+var_names.join('_') } else { var_names[0] }
 | 
			
		||||
	t := p.gen_var_decl(p.var_decl_name, is_static)
 | 
			
		||||
	mut types := [t]
 | 
			
		||||
	mut var_types := [t]
 | 
			
		||||
	// multiple returns types
 | 
			
		||||
	if names.len > 1 {
 | 
			
		||||
		// should we register __ret var?
 | 
			
		||||
		types = t.replace('_V_MulRet_', '').replace('_PTR_', '*').split('_V_')
 | 
			
		||||
	}
 | 
			
		||||
	for i, name in names {
 | 
			
		||||
		var_token_idx := vtoken_idxs[i]
 | 
			
		||||
		if name == '_' {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		typ := types[i]
 | 
			
		||||
		// println('var decl tok=${p.strtok()} ismut=$is_mut')
 | 
			
		||||
		// Don't allow declaring a variable with the same name. Even in a child scope
 | 
			
		||||
		// (shadowing is not allowed)
 | 
			
		||||
		known_var := p.known_var(name)
 | 
			
		||||
		// single var decl, already exists
 | 
			
		||||
		if names.len == 1 && !p.builtin_mod && known_var {
 | 
			
		||||
			p.error_with_token_index('redefinition of `$name`', var_token_idx)
 | 
			
		||||
		}
 | 
			
		||||
		// assignment, but var does not exist
 | 
			
		||||
		if names.len > 1 && is_assign && !known_var {
 | 
			
		||||
			suggested := p.find_misspelled_local_var(name, 50)
 | 
			
		||||
			if suggested != '' {
 | 
			
		||||
				p.error_with_token_index('undefined: `$name`. did you mean:$suggested', var_token_idx)
 | 
			
		||||
			}
 | 
			
		||||
			p.error_with_token_index('undefined: `$name`.', var_token_idx)
 | 
			
		||||
		}
 | 
			
		||||
		if name.len > 1 && contains_capital(name) {
 | 
			
		||||
			p.error_with_token_index('variable names cannot contain uppercase letters, use snake_case instead', var_token_idx)
 | 
			
		||||
	if var_names.len > 1 {
 | 
			
		||||
		var_types = t.replace('_V_MulRet_', '').replace('_PTR_', '*').split('_V_')
 | 
			
		||||
	}
 | 
			
		||||
	// mismatched number of return & assignment vars
 | 
			
		||||
		if names.len != types.len {
 | 
			
		||||
	if var_names.len != var_types.len {
 | 
			
		||||
		mr_fn := p.cgen.cur_line.find_between('=', '(').trim_space()
 | 
			
		||||
			p.error_with_token_index('assignment mismatch: ${names.len} variables but `$mr_fn` returns $types.len values', var_token_idx)
 | 
			
		||||
		p.error_with_token_index('assignment mismatch: ${var_names.len} variables but `$mr_fn` returns $var_types.len values', var_token_idxs.last())
 | 
			
		||||
	}
 | 
			
		||||
	for i, var_name in var_names {
 | 
			
		||||
		var_token_idx := var_token_idxs[i]
 | 
			
		||||
		if var_name == '_' {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		var_is_mut := var_mut[i]
 | 
			
		||||
		var_type := var_types[i]
 | 
			
		||||
		known_var := p.known_var(var_name)
 | 
			
		||||
		// println('var decl tok=${p.strtok()} name=type=$var_name type=$var_type ismut=$var_is_mut')
 | 
			
		||||
		// var decl, already exists (shadowing is not allowed)
 | 
			
		||||
		// Don't allow declaring a variable with the same name. Even in a child scope
 | 
			
		||||
		// if var_names.len == 1 && !p.builtin_mod && known_var {
 | 
			
		||||
		if is_decl_assign && known_var {
 | 
			
		||||
			p.error_with_token_index('redefinition of `$var_name`', var_token_idx)
 | 
			
		||||
		}
 | 
			
		||||
		// mut specified with assignment
 | 
			
		||||
		if /*is_assign && implicit*/ known_var && var_is_mut {
 | 
			
		||||
			p.error_with_token_index('cannot specify mutability for existing var `$var_name`, only for new vars', var_token_idx)
 | 
			
		||||
		}
 | 
			
		||||
		// assignment, but var does not exist
 | 
			
		||||
		if is_assign && !known_var {
 | 
			
		||||
			suggested := p.find_misspelled_local_var(var_name, 50)
 | 
			
		||||
			if suggested != '' {
 | 
			
		||||
				p.error_with_token_index('undefined: `$var_name`. did you mean:$suggested', var_token_idx)
 | 
			
		||||
			}
 | 
			
		||||
			p.error_with_token_index('undefined: `$var_name`.', var_token_idx)
 | 
			
		||||
		}
 | 
			
		||||
		if var_name.len > 1 && contains_capital(var_name) {
 | 
			
		||||
			p.error_with_token_index('variable names cannot contain uppercase letters, use snake_case instead', var_token_idx)
 | 
			
		||||
		}
 | 
			
		||||
		// multiple return
 | 
			
		||||
		if names.len > 1 {
 | 
			
		||||
		if var_names.len > 1 {
 | 
			
		||||
			p.gen(';\n')
 | 
			
		||||
			// assigment
 | 
			
		||||
			if !p.builtin_mod && known_var {
 | 
			
		||||
				v := p.find_var(name) or {
 | 
			
		||||
					p.error_with_token_index('cannot find `$name`', var_token_idx)
 | 
			
		||||
			// if !p.builtin_mod && known_var {
 | 
			
		||||
			if known_var {
 | 
			
		||||
				v := p.find_var(var_name) or {
 | 
			
		||||
					p.error_with_token_index('cannot find `$var_name`', var_token_idx)
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				p.check_types_with_token_index(typ, v.typ, var_token_idx)
 | 
			
		||||
				p.check_types_with_token_index(var_type, v.typ, var_token_idx)
 | 
			
		||||
				if !v.is_mut {
 | 
			
		||||
					p.error_with_token_index('`$v.name` is immutable', var_token_idx)
 | 
			
		||||
				}
 | 
			
		||||
				p.mark_var_used(v)
 | 
			
		||||
				p.mark_var_changed(v)
 | 
			
		||||
				p.gen('$name = ${p.var_decl_name}.var_$i')
 | 
			
		||||
				p.gen('$var_name = ${p.var_decl_name}.var_$i')
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// decleration
 | 
			
		||||
			p.gen('$typ $name = ${p.var_decl_name}.var_$i')
 | 
			
		||||
			p.gen('$var_type $var_name = ${p.var_decl_name}.var_$i')
 | 
			
		||||
		}
 | 
			
		||||
		p.register_var(Var {
 | 
			
		||||
			name: name
 | 
			
		||||
			typ: typ
 | 
			
		||||
			is_mut: is_mut
 | 
			
		||||
			is_alloc: p.is_alloc || typ.starts_with('array_')
 | 
			
		||||
			name: var_name
 | 
			
		||||
			typ: var_type
 | 
			
		||||
			is_mut: var_is_mut
 | 
			
		||||
			is_alloc: p.is_alloc || var_type.starts_with('array_')
 | 
			
		||||
			line_nr: p.tokens[ var_token_idx ].line_nr
 | 
			
		||||
			token_idx: var_token_idx
 | 
			
		||||
		})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue