From f7a93a69f69a7e0d9e66f189f6f46743f3b3595c Mon Sep 17 00:00:00 2001 From: Joe Conigliaro Date: Tue, 10 Mar 2020 22:01:37 +1100 Subject: [PATCH] v: fix/gen multi return/assign + merge VarDecl & AssignStmt --- vlib/v/ast/ast.v | 6 ++-- vlib/v/ast/scope.v | 10 +++--- vlib/v/ast/str.v | 22 +++++++++++-- vlib/v/checker/checker.v | 69 ++++++++++++++++++++++++++++++++-------- vlib/v/eval/eval.v | 11 ++++--- vlib/v/fmt/fmt.v | 30 ++++++----------- vlib/v/gen/cgen.v | 62 +++++++++++++++++++++++------------- vlib/v/gen/jsgen.v | 20 ++++++++---- vlib/v/gen/tests/4.c | 5 ++- vlib/v/gen/tests/4.vv | 4 +-- vlib/v/gen/x64/gen.v | 1 - vlib/v/parser/fn.v | 4 +-- vlib/v/parser/parser.v | 52 +++++++----------------------- 13 files changed, 173 insertions(+), 123 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index aa18661b43..61b8d83a0f 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -16,7 +16,7 @@ AssignExpr | PrefixExpr | MethodCallExpr | IndexExpr | RangeExpr | MatchExpr | CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr | ConcatExpr | Type | AsCast -pub type Stmt = VarDecl | GlobalDecl | FnDecl | Return | Module | Import | ExprStmt | +pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt | ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt | HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt | LineComment | MultiLineComment | AssertStmt | UnsafeStmt @@ -164,6 +164,7 @@ mut: is_c bool muts []bool or_block OrExpr + typ table.Type } pub struct MethodCallExpr { @@ -201,10 +202,9 @@ pub struct Stmt { */ -pub struct VarDecl { +pub struct Var { pub: name string - name2 string // TODO expr Expr is_mut bool mut: diff --git a/vlib/v/ast/scope.v b/vlib/v/ast/scope.v index 3e9b79bb9b..f7518cebc0 100644 --- a/vlib/v/ast/scope.v +++ b/vlib/v/ast/scope.v @@ -10,7 +10,7 @@ mut: start_pos int end_pos int // vars map[string]table.Var - vars map[string]VarDecl + vars map[string]Var } pub fn new_scope(parent &Scope, start_pos int) &Scope { @@ -20,7 +20,7 @@ pub fn new_scope(parent &Scope, start_pos int) &Scope { } } -pub fn (s &Scope) find_scope_and_var(name string) ?(&Scope,VarDecl) { +pub fn (s &Scope) find_scope_and_var(name string) ?(&Scope,Var) { if name in s.vars { return s,s.vars[name] } @@ -32,7 +32,7 @@ pub fn (s &Scope) find_scope_and_var(name string) ?(&Scope,VarDecl) { return none } -pub fn (s &Scope) find_var(name string) ?VarDecl { +pub fn (s &Scope) find_var(name string) ?Var { if name in s.vars { return s.vars[name] } @@ -51,7 +51,7 @@ pub fn (s &Scope) known_var(name string) bool { return false } -pub fn (s mut Scope) register_var(var VarDecl) { +pub fn (s mut Scope) register_var(var Var) { if x := s.find_var(var.name) { // println('existing var: $var.name') return @@ -59,7 +59,7 @@ pub fn (s mut Scope) register_var(var VarDecl) { s.vars[var.name] = var } -pub fn (s mut Scope) override_var(var VarDecl) { +pub fn (s mut Scope) override_var(var Var) { s.vars[var.name] = var } diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 7a1e61abce..ff9b3f87f3 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -78,8 +78,26 @@ pub fn (x Expr) str() string { pub fn (node Stmt) str() string { match node { - VarDecl { - return it.name + ' = ' + it.expr.str() + AssignStmt { + mut out := '' + for i,ident in it.left { + var_info := ident.var_info() + if var_info.is_mut { + out += 'mut ' + } + out += ident.name + if i < it.left.len-1 { + out += ',' + } + } + out += ' $it.op.str() ' + for i,val in it.right { + out += val.str() + if i < it.right.len-1 { + out += ',' + } + } + return out } ExprStmt { return it.expr.str() diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 215c4fe00d..bec2d11cda 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -206,6 +206,7 @@ pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type { if !found { c.error('unknown fn: $fn_name', call_expr.pos) } + call_expr.typ = f.return_type if f.is_c || call_expr.is_c { for expr in call_expr.args { c.expr(expr) @@ -259,12 +260,13 @@ pub fn (c mut Checker) method_call_expr(method_call_expr mut ast.MethodCallExpr) mut scope := c.file.scope.innermost(method_call_expr.pos.pos) or { c.file.scope } - scope.override_var(ast.VarDecl{ + scope.override_var(ast.Var{ name: 'it' typ: array_info.elem_type }) } if method := typ_sym.find_method(name) { + method_call_expr.typ = method.return_type for i, arg_expr in method_call_expr.args { c.expected_type = method.args[i].typ c.expr(arg_expr) @@ -370,27 +372,70 @@ pub fn (c mut Checker) return_stmt(return_stmt ast.Return) { } } -pub fn (c mut Checker) assign_stmt(assign_stmt ast.AssignStmt) { +pub fn (c mut Checker) assign_stmt(assign_stmt mut ast.AssignStmt) { // multi return if assign_stmt.left.len > assign_stmt.right.len { right := c.expr(assign_stmt.right[0]) right_sym := c.table.get_type_symbol(right) - info := right_sym.mr_info() + mr_info := right_sym.mr_info() if right_sym.kind != .multi_return { c.error('wrong number of vars', assign_stmt.pos) } mut scope := c.file.scope.innermost(assign_stmt.pos.pos) or { c.file.scope } - for i, ident in assign_stmt.left { - // TODO: check types - scope.override_var(ast.VarDecl{ + for i, _ in assign_stmt.left { + mut ident := assign_stmt.left[i] + mut var_info := ident.var_info() + val_type := mr_info.types[i] + var_info.typ = val_type + ident.info = var_info + assign_stmt.left[i] = ident + if assign_stmt.op == .assign { + if !c.table.check(val_type, var_info.typ) { + val_type_sym := c.table.get_type_symbol(val_type) + var_type_sym := c.table.get_type_symbol(var_info.typ) + c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', assign_stmt.pos) + } + } + scope.override_var(ast.Var{ name: ident.name - typ: info.types[i] + typ: mr_info.types[i] + }) + } + } + // `a := 1` | `a,b := 1,2` + else { + if assign_stmt.left.len != assign_stmt.right.len { + c.error('wrong number of vars', assign_stmt.pos) + } + for i, _ in assign_stmt.left { + mut ident := assign_stmt.left[i] + val := assign_stmt.right[i] + val_type := c.expr(val) + if assign_stmt.op == .assign { + var_info := ident.var_info() + if !c.table.check(val_type, var_info.typ) { + val_type_sym := c.table.get_type_symbol(val_type) + var_type_sym := c.table.get_type_symbol(var_info.typ) + c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', assign_stmt.pos) + } + } + else if assign_stmt.op == .decl_assign { + mut var_info := ident.var_info() + var_info.typ = val_type + ident.info = var_info + assign_stmt.left[i] = ident + } + mut scope := c.file.scope.innermost(assign_stmt.pos.pos) or { + c.file.scope + } + scope.override_var(ast.Var{ + name: ident.name + typ: val_type }) } } - // TODO: multiple assign } pub fn (c mut Checker) array_init(array_init mut ast.ArrayInit) table.Type { @@ -448,7 +493,7 @@ fn (c mut Checker) stmt(node ast.Stmt) { // c.expected_type = table.void_type match mut node { ast.AssignStmt { - c.assign_stmt(it) + c.assign_stmt(mut it) } // ast.Attr {} // ast.CompIf {} @@ -507,10 +552,6 @@ fn (c mut Checker) stmt(node ast.Stmt) { c.stmt(stmt) } } - ast.VarDecl { - typ := c.expr(it.expr) - it.typ = typ - } else {} // println('checker.stmt(): unhandled node') // println('checker.stmt(): unhandled node (${typeof(node)})') @@ -646,7 +687,7 @@ pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type { } mut found := true mut var_scope := &ast.Scope(0) - mut var := ast.VarDecl{} + mut var := ast.Var{} var_scope,var = start_scope.find_scope_and_var(ident.name) or { found = false c.error('not found: $ident.name - POS: $ident.pos.pos', ident.pos) diff --git a/vlib/v/eval/eval.v b/vlib/v/eval/eval.v index e0284d913d..b74323263d 100644 --- a/vlib/v/eval/eval.v +++ b/vlib/v/eval/eval.v @@ -66,11 +66,14 @@ fn (e mut Eval) stmt(node ast.Stmt) string { // ast.StructDecl { // println('s decl') // } - ast.VarDecl { - e.vars[it.name] = Var{ - value: e.expr(it.expr) - } + ast.AssignStmt { + // TODO; replaced VarDecl } + // ast.VarDecl { + // e.vars[it.name] = Var{ + // value: e.expr(it.expr) + // } + // } else {} } return '>>' diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index f04ac53e4a..cd6ecd187c 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -100,18 +100,22 @@ fn (f mut Fmt) stmts(stmts []ast.Stmt) { fn (f mut Fmt) stmt(node ast.Stmt) { match node { ast.AssignStmt { - for i, left in it.left { - if left.var_info().is_mut { + for i,ident in it.left { + var_info := ident.var_info() + if var_info.is_mut { f.write('mut ') } - f.expr(left) - if i < it.left.len - 1 { + f.write(ident.name) + if i < it.left.len-1 { f.write(', ') } } f.write(' $it.op.str() ') - for right in it.right { - f.expr(right) + for i,val in it.right { + f.expr(val) + if i < it.right.len-1 { + f.write(', ') + } } f.writeln('') } @@ -231,20 +235,6 @@ fn (f mut Fmt) stmt(node ast.Stmt) { f.stmts(it.stmts) f.writeln('}') } - ast.VarDecl { - // type_sym := f.table.get_type_symbol(it.typ) - if it.is_mut { - f.write('mut ') - } - if it.name2 == '' { - f.write('$it.name := ') - } - else { - f.write('/*2*/$it.name, $it.name2 := ') - } - f.expr(it.expr) - f.writeln('') - } ast.Import { // already handled in f.imports } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 9740bcc2b1..82facea3ca 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -135,22 +135,7 @@ fn (g mut Gen) stmt(node ast.Stmt) { // g.writeln('//// stmt start') match node { ast.AssignStmt { - // ident0 := it.left[0] - // info0 := ident0.var_info() - // for i, ident in it.left { - // info := ident.var_info() - // if info0.typ.typ.kind == .multi_return { - // if i == 0 { - // g.write('$info.typ.typ.name $ident.name = ') - // g.expr(it.right[0]) - // } else { - // arg_no := i-1 - // g.write('$info.typ.typ.name $ident.name = $ident0.name->arg[$arg_no]') - // } - // } - // g.writeln(';') - // } - g.write('') // /*assign*/') + g.gen_assign_stmt(it) } ast.AssertStmt { g.write('// assert') @@ -283,18 +268,51 @@ fn (g mut Gen) stmt(node ast.Stmt) { ast.UnsafeStmt { g.stmts(it.stmts) } - ast.VarDecl { - styp := g.typ(it.typ) - g.write('$styp $it.name = ') - g.expr(it.expr) - g.writeln(';') - } else { verror('cgen.stmt(): unhandled node ' + typeof(node)) } } } +fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { + // multi return + if assign_stmt.left.len > assign_stmt.right.len { + mut return_type := table.void_type + match assign_stmt.right[0] { + ast.CallExpr { + return_type = it.typ + } + ast.MethodCallExpr { + return_type = it.typ + } + else { + panic('expected call') + } + } + mr_typ_sym := g.table.get_type_symbol(return_type) + mr_var_name := 'mr_$assign_stmt.pos.pos' + g.write('$mr_typ_sym.name $mr_var_name = ') + g.expr(assign_stmt.right[0]) + g.writeln(';') + for i, ident in assign_stmt.left { + ident_var_info := ident.var_info() + var_type_sym := g.table.get_type_symbol(ident_var_info.typ) + g.writeln('$var_type_sym.name $ident.name = $mr_var_name->arg[$i];') + } + } + // `a := 1` | `a,b := 1,2` + else { + for i, ident in assign_stmt.left { + val := assign_stmt.right[i] + ident_var_info := ident.var_info() + var_type_sym := g.table.get_type_symbol(ident_var_info.typ) + g.write('$var_type_sym.name $ident.name = ') + g.expr(val) + g.writeln(';') + } + } +} + fn (g mut Gen) gen_fn_decl(it ast.FnDecl) { if it.is_c || it.name == 'malloc' || it.no_body { return diff --git a/vlib/v/gen/jsgen.v b/vlib/v/gen/jsgen.v index 9fb38b8b68..4611a665b8 100644 --- a/vlib/v/gen/jsgen.v +++ b/vlib/v/gen/jsgen.v @@ -57,12 +57,20 @@ fn (g mut JsGen) stmt(node ast.Stmt) { } g.writeln(';') } - ast.AssignStmt {} - ast.VarDecl { - type_sym := g.table.get_type_symbol(it.typ) - g.write('var /* $type_sym.name */ $it.name = ') - g.expr(it.expr) - g.writeln(';') + ast.AssignStmt { + if it.left.len > it.right.len { + // TODO: multi return + } + else { + for i,ident in it.left { + var_info := ident.var_info() + var_type_sym := g.table.get_type_symbol(var_info.typ) + val := it.right[i] + g.write('var /* $var_type_sym.name */ $ident.name = ') + g.expr(val) + g.writeln(';') + } + } } ast.ForStmt { g.write('while (') diff --git a/vlib/v/gen/tests/4.c b/vlib/v/gen/tests/4.c index 4fb0cc96af..426b1f1f2e 100644 --- a/vlib/v/gen/tests/4.c +++ b/vlib/v/gen/tests/4.c @@ -54,7 +54,10 @@ int main() { string foo_a = af_idx_el.a; map_string_string m1 = new_map(1, sizeof(string)); map_string_int m2 = new_map_init(2, sizeof(int), (string[2]){tos3("v"), tos3("lang"), }, (int[2]){1, 2, }); - return 0; + multi_return_int_string mr_548 = mr_test(); + int mr1 = mr_548->arg[0]; + string mr2 = mr_548->arg[1]; + return 0; } multi_return_int_string mr_test() { diff --git a/vlib/v/gen/tests/4.vv b/vlib/v/gen/tests/4.vv index 0ef5a679af..c3c874b73d 100644 --- a/vlib/v/gen/tests/4.vv +++ b/vlib/v/gen/tests/4.vv @@ -31,8 +31,6 @@ fn main() { mut f := [testa(),2,3,4] mut g := [testb(1),'hello'] - mr1, mr2 := mr_test() - //mut arr_foo := []Foo arr_foo := [a] //arr_foo << a // TODO @@ -41,6 +39,8 @@ fn main() { mut m1 := map[string]string mut m2 := {'v': 1, 'lang': 2} + + mr1, mr2 := mr_test() } fn mr_test() (int, string) { diff --git a/vlib/v/gen/x64/gen.v b/vlib/v/gen/x64/gen.v index 5151067bbb..ec7e39a505 100644 --- a/vlib/v/gen/x64/gen.v +++ b/vlib/v/gen/x64/gen.v @@ -332,7 +332,6 @@ fn (g mut Gen) stmt(node ast.Stmt) { } ast.Return {} ast.AssignStmt {} - ast.VarDecl {} ast.ForStmt {} ast.StructDecl {} ast.ExprStmt { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 6d2a881b26..9d07339ef3 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -88,7 +88,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { // name: rec_name // typ: rec_type // }) - p.scope.register_var(ast.VarDecl{ + p.scope.register_var(ast.Var{ name: rec_name typ: rec_type }) @@ -115,7 +115,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { typ: ast_arg.typ } args << var - p.scope.register_var(ast.VarDecl{ + p.scope.register_var(ast.Var{ name: ast_arg.name typ: ast_arg.typ }) diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 54c2b29957..50ce1802a9 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -296,7 +296,7 @@ pub fn (p mut Parser) stmt() ast.Stmt { } } .key_mut { - return p.var_decl_and_assign_stmt() + return p.assign_stmt() } .key_for { return p.for_statement() @@ -341,7 +341,7 @@ pub fn (p mut Parser) stmt() ast.Stmt { else { // `x := ...` if p.tok.kind == .name && p.peek_tok.kind in [.decl_assign, .comma] { - return p.var_decl_and_assign_stmt() + return p.assign_stmt() } // `label:` else if p.tok.kind == .name && p.peek_tok.kind == .colon { @@ -870,7 +870,7 @@ fn (p mut Parser) index_expr(left ast.Expr) ast.IndexExpr { } fn (p mut Parser) filter() { - p.scope.register_var(ast.VarDecl{ + p.scope.register_var(ast.Var{ name: 'it' }) } @@ -990,7 +990,7 @@ fn (p mut Parser) for_statement() ast.Stmt { // mut inc := ast.Stmt{} mut inc := ast.Expr{} if p.peek_tok.kind in [.assign, .decl_assign] { - init = p.var_decl_and_assign_stmt() + init = p.assign_stmt() } else if p.tok.kind != .semicolon {} // allow `for ;; i++ {` @@ -1029,7 +1029,7 @@ fn (p mut Parser) for_statement() ast.Stmt { if p.tok.kind == .comma { p.check(.comma) val_name = p.check_name() - p.scope.register_var(ast.VarDecl{ + p.scope.register_var(ast.Var{ name: val_name typ: table.int_type }) @@ -1048,7 +1048,7 @@ fn (p mut Parser) for_statement() ast.Stmt { high_expr = p.expr(0) } // TODO: update var type in checker - p.scope.register_var(ast.VarDecl{ + p.scope.register_var(ast.Var{ name: var_name // expr: cond @@ -1093,7 +1093,7 @@ fn (p mut Parser) if_expr() ast.Expr { var_name := p.check_name() p.check(.decl_assign) expr := p.expr(0) - p.scope.register_var(ast.VarDecl{ + p.scope.register_var(ast.Var{ name: var_name expr: expr }) @@ -1508,50 +1508,22 @@ fn (p mut Parser) parse_assign_rhs() []ast.Expr { return exprs } -fn (p mut Parser) var_decl_and_assign_stmt() ast.Stmt { +fn (p mut Parser) assign_stmt() ast.Stmt { idents := p.parse_assign_lhs() op := p.tok.kind p.next() // :=, = exprs := p.parse_assign_rhs() is_decl := op == .decl_assign - // VarDecl - if idents.len == 1 { - ident := idents[0] - expr := exprs[0] - info0 := ident.var_info() + for ident in idents { known_var := p.scope.known_var(ident.name) if !is_decl && !known_var { p.error('unknown variable `$ident.name`') } - if is_decl && ident.kind != .blank_ident { - if known_var { - p.error('redefinition of `$ident.name`') - } - p.scope.register_var(ast.VarDecl{ - name: ident.name - expr: expr - }) - } - return ast.VarDecl{ - name: ident.name - // name2: name2 - - expr: expr // p.expr(token.lowest_prec) - - is_mut: info0.is_mut - // typ: typ - - pos: p.tok.position() - } - // return p.var_decl(ident[0], exprs[0]) - } - // AssignStmt - for ident in idents { if is_decl && ident.kind != .blank_ident { if p.scope.known_var(ident.name) { p.error('redefinition of `$ident.name`') } - p.scope.register_var(ast.VarDecl{ + p.scope.register_var(ast.Var{ name: ident.name }) } @@ -1564,8 +1536,6 @@ fn (p mut Parser) var_decl_and_assign_stmt() ast.Stmt { } } -// pub fn (p mut Parser) assign_stmt() ast.AssignStmt {} -// fn (p mut Parser) var_decl() ast.VarDecl {} fn (p mut Parser) hash() ast.HashStmt { val := p.tok.lit p.next() @@ -1633,7 +1603,7 @@ fn (p mut Parser) match_expr() ast.MatchExpr { exprs << ast.Type{ typ: typ } - p.scope.register_var(ast.VarDecl{ + p.scope.register_var(ast.Var{ name: 'it' typ: typ })