From 60d932e57d9cc8aee81ac286376e32ca75518ad8 Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Mon, 23 Sep 2019 12:45:19 +1000 Subject: [PATCH] compiler: functions with multiple returns --- compiler/fn.v | 16 ++++++- compiler/parser.v | 114 ++++++++++++++++++++++++++++++++++++---------- vc | 1 + 3 files changed, 106 insertions(+), 25 deletions(-) create mode 160000 vc diff --git a/compiler/fn.v b/compiler/fn.v index 6756547aef..f2b01bec97 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -279,12 +279,26 @@ fn (p mut Parser) fn_decl() { // Returns a type? mut typ := 'void' if p.tok == .name || p.tok == .mul || p.tok == .amp || p.tok == .lsbr || - p.tok == .question { + p.tok == .question || p.tok == .lpar { p.fgen(' ') // TODO In // if p.tok in [ .name, .mul, .amp, .lsbr ] { typ = p.get_type() } + // multiple returns + if typ.starts_with('MultiReturn_') { + if !p.first_pass() && !p.table.known_type(typ) { + p.table.register_type2(Type{ + cat: TypeCategory.struct_, + name: typ, + mod: p.mod + }) + for i, t in typ.replace('MultiReturn_', '').replace('0ptr0', '*').split('_') { + p.table.add_field(typ, 'var_$i', t, false, '', .public) + } + p.cgen.typedefs << 'typedef struct $typ $typ;' + } + } // Translated C code can have empty functions (just definitions) is_fn_header := !is_c && !is_sig && (p.pref.translated || p.pref.is_test) && p.tok != .lcbr if is_fn_header { diff --git a/compiler/parser.v b/compiler/parser.v index 7713e3883c..e78e7f2505 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -833,6 +833,25 @@ fn (p mut Parser) get_type() string { mut mul := false mut nr_muls := 0 mut typ := '' + // multiple returns + if p.tok == .lpar { + // if p.inside_tuple { + // p.error('unexpected (') + // } + // p.inside_tuple = true + p.check(.lpar) + mut types := []string + for { + types << p.get_type() + if p.tok != .comma { + break + } + p.check(.comma) + } + p.check(.rpar) + // p.inside_tuple = false + return 'MultiReturn_' + types.join('_').replace('*', '0ptr0') + } // fn type if p.tok == .func { mut f := Fn{name: '_', mod: p.mod} @@ -1162,8 +1181,9 @@ fn (p mut Parser) statement(add_semi bool) string { p.check(.colon) return '' } - // `a := 777` - else if p.peek() == .decl_assign { + // `a := 777` + else if p.peek() == .decl_assign || p.peek() == .comma { + p.log('var decl') p.var_decl() } else { @@ -1324,29 +1344,55 @@ fn (p mut Parser) var_decl() { p.check(.key_static) p.fspace() } - // println('var decl tok=${p.strtok()} ismut=$is_mut') - var_scanner_pos := p.scanner.get_scanner_pos() - name := p.check_name() - p.var_decl_name = name - // Don't allow declaring a variable with the same name. Even in a child scope - // (shadowing is not allowed) - if !p.builtin_mod && p.cur_fn.known_var(name) { - v := p.cur_fn.find_var(name) - p.error('redefinition of `$name`') - } - if name.len > 1 && contains_capital(name) { - p.error('variable names cannot contain uppercase letters, use snake_case instead') + + mut names := []string + names << p.check_name() + for p.tok == .comma { + p.check(.comma) + names << p.check_name() } + mr_var_name := if names.len > 1 { '__ret_'+names.join('_') } else { names[0] } p.check_space(.decl_assign) // := - typ := p.gen_var_decl(name, is_static) - p.register_var(Var { - name: name - typ: typ - is_mut: is_mut - is_alloc: p.is_alloc || typ.starts_with('array_') - scanner_pos: var_scanner_pos - line_nr: var_scanner_pos.line_nr - }) + // t := p.bool_expression() + p.var_decl_name = mr_var_name + t := p.gen_var_decl(mr_var_name, is_static) + + mut types := [t] + // multiple returns + if names.len > 1 { + // should we register __ret var? + types = t.replace('MultiReturn_', '').replace('0ptr0', '*').split('_') + } + for i, name in names { + typ := types[i] + if names.len > 1 { + p.gen(';\n') + p.gen('$typ $name = ${mr_var_name}.var_$i') + } + // println('var decl tok=${p.strtok()} ismut=$is_mut') + var_scanner_pos := p.scanner.get_scanner_pos() + // name := p.check_name() + // p.var_decl_name = name + // Don't allow declaring a variable with the same name. Even in a child scope + // (shadowing is not allowed) + if !p.builtin_mod && p.cur_fn.known_var(name) { + // v := p.cur_fn.find_var(name) + p.error('redefinition of `$name`') + } + if name.len > 1 && contains_capital(name) { + p.error('variable names cannot contain uppercase letters, use snake_case instead') + } + // p.check_space(.decl_assign) // := + // typ := p.gen_var_decl(name, is_static) + p.register_var(Var { + name: name + typ: typ + is_mut: is_mut + is_alloc: p.is_alloc || typ.starts_with('array_') + scanner_pos: var_scanner_pos + line_nr: var_scanner_pos.line_nr + }) + } //if p.is_alloc { println('REG VAR IS ALLOC $name') } p.var_decl_name = '' p.is_empty_c_struct_init = false @@ -3532,7 +3578,27 @@ fn (p mut Parser) return_st() { p.inside_return_expr = true is_none := p.tok == .key_none p.expected_type = p.cur_fn.typ - expr_type := p.bool_expression() + // expr_type := p.bool_expression() + mut expr_type := p.bool_expression() + mut types := []string + types << expr_type + for p.tok == .comma { + p.check(.comma) + types << p.bool_expression() + } + // multiple returns + if types.len > 1 { + expr_type = 'MultiReturn_' + types.join('_').replace('*', '0ptr0') + ret_vals := p.cgen.cur_line.right(ph) + mut ret_fields := '' + for ret_val_idx, ret_val in ret_vals.split(' ') { + if ret_val_idx > 0 { + ret_fields += ',' + } + ret_fields += '.var_$ret_val_idx=$ret_val' + } + p.cgen.resetln('($expr_type){$ret_fields}') + } p.inside_return_expr = false // Automatically wrap an object inside an option if the function // returns an option diff --git a/vc b/vc new file mode 160000 index 0000000000..c2c2641919 --- /dev/null +++ b/vc @@ -0,0 +1 @@ +Subproject commit c2c26419198a271ca00cb8d77119d17c5622569e