~mna/snow

c5fbe8f6de8dad9557e370cc4613bbe177ec7098 — Martin Angers 1 year, 8 months ago b8b74f0 + a675757
Merge branch 'wip-group-var-let'
68 files changed, 638 insertions(+), 379 deletions(-)

M pkg/ast/ast.go
M pkg/ast/visitor.go
A pkg/codegen/testdata/fn_multi_vars_per_decl.snow
A pkg/codegen/testdata/fn_multi_vars_per_decl.snow.err
A pkg/codegen/testdata/fn_multi_vars_per_decl.snow.want
M pkg/grammar/grammar.ebnf
M pkg/parser/parser.go
M pkg/parser/testdata/fn_assign.snow.want
M pkg/parser/testdata/fn_compare_ops.snow.want
M pkg/parser/testdata/fn_invalid_empty_tuple.snow.want
M pkg/parser/testdata/fn_nested_struct_type.snow.want
M pkg/parser/testdata/fn_nested_tuples.snow.want
M pkg/parser/testdata/fn_ref_method.snow.want
M pkg/parser/testdata/fn_struct_selector.snow.want
M pkg/parser/testdata/fn_type_literal.snow.want
M pkg/parser/testdata/guard_else.snow.want
M pkg/parser/testdata/guard_nested.snow.want
M pkg/parser/testdata/guard_no_body.snow.want
M pkg/parser/testdata/guard_no_else.snow.want
M pkg/parser/testdata/if.snow.want
M pkg/parser/testdata/if_else.snow.want
M pkg/parser/testdata/if_else_if.snow.want
M pkg/parser/testdata/if_else_if_else.snow.want
M pkg/parser/testdata/if_nested.snow.want
M pkg/parser/testdata/if_no_block.snow.want
M pkg/parser/testdata/let.snow.want
M pkg/parser/testdata/let_full.snow.want
M pkg/parser/testdata/let_infer.snow.want
M pkg/parser/testdata/let_kwonly.snow.err
M pkg/parser/testdata/let_kwonly.snow.want
M pkg/parser/testdata/let_question.snow.want
A pkg/parser/testdata/multi_args_invalid_trailing_comma.snow
A pkg/parser/testdata/multi_args_invalid_trailing_comma.snow.err
A pkg/parser/testdata/multi_args_invalid_trailing_comma.snow.want
A pkg/parser/testdata/multi_args_per_decl.snow
A pkg/parser/testdata/multi_args_per_decl.snow.err
A pkg/parser/testdata/multi_args_per_decl.snow.want
M pkg/parser/testdata/struct_decl.snow.want
M pkg/parser/testdata/var.snow.want
M pkg/parser/testdata/var_bang.snow.want
M pkg/parser/testdata/var_comma_for_semi.snow.err
M pkg/parser/testdata/var_comma_for_semi.snow.want
M pkg/parser/testdata/var_full.snow.want
M pkg/parser/testdata/var_infer.snow.want
M pkg/parser/testdata/var_invalid_empty_tuple.snow.want
M pkg/parser/testdata/var_invalid_type.snow.want
M pkg/parser/testdata/var_kw_as_ident.snow.want
M pkg/parser/testdata/var_kwonly.snow.err
M pkg/parser/testdata/var_kwonly.snow.want
M pkg/parser/testdata/var_missing_type_init.snow.err
M pkg/parser/testdata/var_missing_type_init.snow.want
M pkg/parser/testdata/var_op_precedence.snow.want
M pkg/parser/testdata/var_paren_type.snow.want
M pkg/parser/testdata/var_paren_type_trail_comma.snow.want
M pkg/parser/testdata/var_tuple.snow.want
M pkg/parser/testdata/var_tuple_type.snow.want
M pkg/printer/printer.go
M pkg/printer/printer_test.go
A pkg/semantic/testdata/check/fn_multi_vars_per_decl.snow.err
A pkg/semantic/testdata/check/fn_multi_vars_per_decl.snow.want
A pkg/semantic/testdata/fn_multi_vars_per_decl.snow
A pkg/semantic/testdata/scopes/fn_multi_vars_per_decl.snow.err
A pkg/semantic/testdata/scopes/fn_multi_vars_per_decl.snow.want
A pkg/semantic/testdata/static/fn_multi_vars_per_decl.snow.err
A pkg/semantic/testdata/static/fn_multi_vars_per_decl.snow.want
A pkg/semantic/testdata/types/fn_multi_vars_per_decl.snow.err
A pkg/semantic/testdata/types/fn_multi_vars_per_decl.snow.want
M pkg/semantic/translate_pass.go
M pkg/ast/ast.go => pkg/ast/ast.go +25 -11
@@ 48,13 48,9 @@ type (

	// VarDecl represents a VarDecl production.
	VarDecl struct {
		Kw     token.Token
		KwPos  token.Pos
		Name   *Ident
		Colon  token.Pos // token.NoPos if no colon (no explicit type)
		Type   Expr      // may be nil
		Assign token.Pos // token.NoPos if no value assigned
		Value  Expr
		Kw    token.Token
		KwPos token.Pos
		Vars  []*VarDef // at least one
	}

	// StructDecl represents a StructDecl production.


@@ 207,6 203,16 @@ type (
		Comma token.Pos // token.NoPos if no trailing comma
	}

	// VarDef represents a variable definition in a VarDecl.
	VarDef struct {
		Name   *Ident
		Colon  token.Pos // token.NoPos if no colon (no explicit type)
		Type   Expr      // may be nil
		Assign token.Pos // token.NoPos if no value assigned
		Value  Expr      // may be nil
		Comma  token.Pos // token.NoPos if no trailing comma
	}

	// Attr represents a function attribute.
	Attr struct {
		At     token.Pos


@@ 378,6 384,17 @@ func (k *KeyValue) End() token.Pos {

func (v *VarDecl) Pos() token.Pos { return v.KwPos }
func (v *VarDecl) End() token.Pos {
	if len(v.Vars) > 0 {
		return v.Vars[len(v.Vars)-1].End()
	}
	return token.Pos(int(v.KwPos) + len(v.Kw.String()))
}

func (v *VarDef) Pos() token.Pos { return v.Name.Pos() }
func (v *VarDef) End() token.Pos {
	if v.Comma.IsValid() {
		return v.Comma + 1
	}
	if v.Value != nil {
		return v.Value.End()
	}


@@ 388,10 405,7 @@ func (v *VarDecl) End() token.Pos {
	if v.Colon.IsValid() {
		return v.Colon + 1
	}
	if v.Name != nil {
		return v.Name.End()
	}
	return token.Pos(int(v.KwPos) + len(v.Kw.String()))
	return v.Name.End()
}

func (s *StructDecl) Pos() token.Pos { return s.Struct }

M pkg/ast/visitor.go => pkg/ast/visitor.go +5 -0
@@ 90,6 90,11 @@ func Walk(v Visitor, node Node) {
		}

	case *VarDecl:
		for _, vr := range n.Vars {
			Walk(v, vr)
		}

	case *VarDef:
		if n.Name != nil {
			Walk(v, n.Name)
		}

A pkg/codegen/testdata/fn_multi_vars_per_decl.snow => pkg/codegen/testdata/fn_multi_vars_per_decl.snow +9 -0
@@ 0,0 1,9 @@
fn main() {
  let x = 1, y: i64 = 2
  println(x + y)
}

@extern(import: "fmt", symbol: "Println")
fn println(x: i64)

#=3

A pkg/codegen/testdata/fn_multi_vars_per_decl.snow.err => pkg/codegen/testdata/fn_multi_vars_per_decl.snow.err +0 -0
A pkg/codegen/testdata/fn_multi_vars_per_decl.snow.want => pkg/codegen/testdata/fn_multi_vars_per_decl.snow.want +15 -0
@@ 0,0 1,15 @@
package main

import "fmt"

func main() {
	var x int = 1
	var y int64 = int64(2)
	_اprintln(int64(x) + y)
}

func _اprintln(
	x int64,
) {
	fmt.Println(x)
}

M pkg/grammar/grammar.ebnf => pkg/grammar/grammar.ebnf +3 -1
@@ 18,7 18,9 @@ Declaration = VarDecl | FuncDecl | StructDecl .
SimpleStmt = ExprStmt | AssignStmt .

// => Variable declaration statement
VarDecl = ( "let" | "var" ) ident ( TypedVarDecl | InitializedVarDecl ) .
VarDecl = ( "let" | "var" ) VarList . // there is no semicolon inserted after a comma, so cannot have a trailing comma
VarList = Var { "," Var } .
Var = ident ( TypedVarDecl | InitializedVarDecl ) .
TypedVarDecl = ":" Type [ InitializedVarDecl ] .
InitializedVarDecl = "=" Expr .


M pkg/parser/parser.go => pkg/parser/parser.go +34 -16
@@ 215,28 215,46 @@ func (p *parser) parseVarDecl() *ast.VarDecl {
		KwPos: p.pos,
	}

	pos := p.pos
	p.next()
	vd.Name = p.parseIdent()

	if p.tok == token.Colon {
		vd.Colon = p.pos
		p.next()
		vd.Type = p.parseType()
	}
	if p.tok == token.Assign {
		vd.Assign = p.pos
		p.next()
		vd.Value = p.parseExpr()
	vars := p.parseVarDefList()
	if len(vars) == 0 {
		p.expect(token.Ident)
	}
	p.expectSemi()
	vd.Vars = vars

	if vd.Type == nil && vd.Value == nil {
		p.error(pos, "missing variable type or initialization")
	}
	return vd
}

func (p *parser) parseVarDefList() []*ast.VarDef {
	var list []*ast.VarDef
	for !tokenIn(p.tok, token.EOF, token.Semicolon) {
		pos := p.pos
		vd := &ast.VarDef{
			Name: p.parseIdent(),
		}
		if p.tok == token.Colon {
			vd.Colon = p.expect(token.Colon)
			vd.Type = p.parseType()
		}
		if p.tok == token.Assign {
			vd.Assign = p.expect(token.Assign)
			vd.Value = p.parseExpr()
		}
		if vd.Type == nil && vd.Value == nil {
			p.error(pos, "missing variable type or initialization")
		}
		list = append(list, vd)

		if p.tok == token.Comma {
			vd.Comma = p.expect(token.Comma)
		} else {
			break
		}
	}
	return list
}

func (p *parser) parseIdentOrTupleIndex() *ast.Ident {
	if p.tok == token.Int {
		// must be a base10 integer (i.e. not 0xabc nor 0b1101)


@@ 268,7 286,7 @@ func (p *parser) parseIdent() *ast.Ident {
func (p *parser) parseBasicLit() *ast.BasicLit {
	var lit string
	tok := token.Illegal
	if p.tok == token.String || p.tok == token.Int {
	if tokenIn(p.tok, token.String, token.Int) {
		lit = p.lit
		tok = p.tok
	}

M pkg/parser/testdata/fn_assign.snow.want => pkg/parser/testdata/fn_assign.snow.want +4 -3
@@ 6,9 6,10 @@ file [1, #0] [0:40]
        ident [x] [8:9]
        ident [int] [11:14]
    block [2] [16:40]
      var: [20:30]
        ident [y] [24:25]
        ident [int] [27:30]
      var [20:30]
        y: [24:30]
          ident [y] [24:25]
          ident [int] [27:30]
      assign [33:38]
        ident [y] [33:34]
        ident [x] [37:38]

M pkg/parser/testdata/fn_compare_ops.snow.want => pkg/parser/testdata/fn_compare_ops.snow.want +25 -24
@@ 3,27 3,28 @@ file [1, #0] [0:81]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [1] [10:81]
      let= [14:79]
        ident [a] [18:19]
        binary [||] [22:79]
          binary [||] [22:70]
            binary [!=] [22:53]
              binary [==] [22:48]
                binary [<=] [22:43]
                  binary [>=] [22:38]
                    binary [<] [22:33]
                      binary [>] [22:28]
                        int [1] [22:23]
                        unary [-] [26:28]
                          int [2] [27:28]
                      unary [+] [31:33]
                        int [3] [32:33]
                    int [4] [37:38]
                  int [5] [42:43]
                int [6] [47:48]
              int [7] [52:53]
            binary [&&] [57:70]
              ident [true] [57:61]
              ident [false] [65:70]
          unary [!] [74:79]
            ident [true] [75:79]
      let [14:79]
        a= [18:79]
          ident [a] [18:19]
          binary [||] [22:79]
            binary [||] [22:70]
              binary [!=] [22:53]
                binary [==] [22:48]
                  binary [<=] [22:43]
                    binary [>=] [22:38]
                      binary [<] [22:33]
                        binary [>] [22:28]
                          int [1] [22:23]
                          unary [-] [26:28]
                            int [2] [27:28]
                        unary [+] [31:33]
                          int [3] [32:33]
                      int [4] [37:38]
                    int [5] [42:43]
                  int [6] [47:48]
                int [7] [52:53]
              binary [&&] [57:70]
                ident [true] [57:61]
                ident [false] [65:70]
            unary [!] [74:79]
              ident [true] [75:79]

M pkg/parser/testdata/fn_invalid_empty_tuple.snow.want => pkg/parser/testdata/fn_invalid_empty_tuple.snow.want +4 -3
@@ 3,6 3,7 @@ file [1, #0] [0:26]
    ident [main] [3:7]
    sig [0->0] [7:9]
    block [1] [10:26]
      let= [14:24]
        ident [x] [18:19]
        bad expr [22:24]
      let [14:24]
        x= [18:24]
          ident [x] [18:19]
          bad expr [22:24]

M pkg/parser/testdata/fn_nested_struct_type.snow.want => pkg/parser/testdata/fn_nested_struct_type.snow.want +8 -7
@@ 9,10 9,11 @@ file [1, #0] [0:92]
          ident [T] [36:37]
          struct [0] [46:64]
            ident [U] [53:54]
      var: [78:90]
        ident [u] [82:83]
        select [85:90]
          select [85:88]
            ident [S] [85:86]
            ident [T] [87:88]
          ident [U] [89:90]
      var [78:90]
        u: [82:90]
          ident [u] [82:83]
          select [85:90]
            select [85:88]
              ident [S] [85:86]
              ident [T] [87:88]
            ident [U] [89:90]

M pkg/parser/testdata/fn_nested_tuples.snow.want => pkg/parser/testdata/fn_nested_tuples.snow.want +30 -26
@@ 3,29 3,33 @@ file [1, #0] [0:79]
    ident [main] [3:7]
    sig [0->0] [7:9]
    block [4] [10:79]
      var= [14:28]
        ident [a] [18:19]
        tuple value [2] [22:28]
          item [23:25]
            int [1] [23:24]
          item [26:27]
            int [2] [26:27]
      var= [31:47]
        ident [b] [35:36]
        tuple value [2] [39:47]
          item [40:44]
            string ["x"] [40:43]
          item [45:46]
            ident [a] [45:46]
      var= [50:61]
        ident [c] [54:55]
        select [58:61]
          ident [a] [58:59]
          ident [0] [60:61]
      var= [64:77]
        ident [d] [68:69]
        select [72:77]
          select [72:75]
            ident [b] [72:73]
            ident [1] [74:75]
          ident [1] [76:77]
      var [14:28]
        a= [18:28]
          ident [a] [18:19]
          tuple value [2] [22:28]
            item [23:25]
              int [1] [23:24]
            item [26:27]
              int [2] [26:27]
      var [31:47]
        b= [35:47]
          ident [b] [35:36]
          tuple value [2] [39:47]
            item [40:44]
              string ["x"] [40:43]
            item [45:46]
              ident [a] [45:46]
      var [50:61]
        c= [54:61]
          ident [c] [54:55]
          select [58:61]
            ident [a] [58:59]
            ident [0] [60:61]
      var [64:77]
        d= [68:77]
          ident [d] [68:69]
          select [72:77]
            select [72:75]
              ident [b] [72:73]
              ident [1] [74:75]
            ident [1] [76:77]

M pkg/parser/testdata/fn_ref_method.snow.want => pkg/parser/testdata/fn_ref_method.snow.want +4 -3
@@ 1,9 1,10 @@
file [1, #0] [0:60]
  struct [2] [0:60]
    ident [S] [7:8]
    var: [13:23]
      ident [x] [17:18]
      ident [int] [20:23]
    var [13:23]
      x: [17:23]
        ident [x] [17:18]
        ident [int] [20:23]
    ref fn [26:58]
      ident [inc] [33:36]
      sig [0->0] [36:38]

M pkg/parser/testdata/fn_struct_selector.snow.want => pkg/parser/testdata/fn_struct_selector.snow.want +8 -6
@@ 1,16 1,18 @@
file [2, #0] [0:65]
  struct [1] [0:29]
    ident [Point] [7:12]
    var: [17:27]
      ident [x] [21:22]
      ident [int] [24:27]
    var [17:27]
      x: [21:27]
        ident [x] [21:22]
        ident [int] [24:27]
  fn [31:65]
    ident [test] [34:38]
    sig [0->0] [38:40]
    block [2] [41:65]
      var: [45:57]
        ident [p] [49:50]
        ident [Point] [52:57]
      var [45:57]
        p: [49:57]
          ident [p] [49:50]
          ident [Point] [52:57]
      expr [60:63]
        select [60:63]
          ident [p] [60:61]

M pkg/parser/testdata/fn_type_literal.snow.want => pkg/parser/testdata/fn_type_literal.snow.want +9 -8
@@ 18,14 18,15 @@ file [2, #0] [0:101]
    ident [test] [53:57]
    sig [0->0] [57:59]
    block [2] [60:101]
      var: [64:88]
        ident [f] [68:69]
        sig [2->1] [71:88]
          param [72:76]
            ident [int] [72:75]
          param [77:80]
            ident [int] [77:80]
          ident [int] [85:88]
      var [64:88]
        f: [68:88]
          ident [f] [68:69]
          sig [2->1] [71:88]
            param [72:76]
              ident [int] [72:75]
            param [77:80]
              ident [int] [77:80]
            ident [int] [85:88]
      assign [92:99]
        ident [f] [92:93]
        ident [add] [96:99]

M pkg/parser/testdata/guard_else.snow.want => pkg/parser/testdata/guard_else.snow.want +4 -3
@@ 3,9 3,10 @@ file [1, #0] [0:45]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [2] [10:45]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      guard [1] [28:43]
        item [34:35]
          ident [x] [34:35]

M pkg/parser/testdata/guard_nested.snow.want => pkg/parser/testdata/guard_nested.snow.want +8 -6
@@ 3,12 3,14 @@ file [1, #0] [0:88]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [3] [10:88]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var: [28:39]
        ident [y] [32:33]
        ident [bool] [35:39]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      var [28:39]
        y: [32:39]
          ident [y] [32:33]
          ident [bool] [35:39]
      guard [1] [43:86]
        item [49:50]
          ident [x] [49:50]

M pkg/parser/testdata/guard_no_body.snow.want => pkg/parser/testdata/guard_no_body.snow.want +4 -3
@@ 3,9 3,10 @@ file [1, #0] [0:43]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [2] [10:43]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      guard [1] [28:43]
        item [34:35]
          ident [x] [34:35]

M pkg/parser/testdata/guard_no_else.snow.want => pkg/parser/testdata/guard_no_else.snow.want +4 -3
@@ 3,9 3,10 @@ file [1, #0] [0:45]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [2] [10:45]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      guard [1] [29:45]
        item [35:36]
          ident [x] [35:36]

M pkg/parser/testdata/if.snow.want => pkg/parser/testdata/if.snow.want +4 -3
@@ 3,9 3,10 @@ file [1, #0] [0:49]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [3] [10:49]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      if [1] [28:38]
        item [31:32]
          ident [x] [31:32]

M pkg/parser/testdata/if_else.snow.want => pkg/parser/testdata/if_else.snow.want +4 -3
@@ 3,9 3,10 @@ file [1, #0] [0:60]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [3] [10:60]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      if [1] [28:49]
        item [31:32]
          ident [x] [31:32]

M pkg/parser/testdata/if_else_if.snow.want => pkg/parser/testdata/if_else_if.snow.want +8 -6
@@ 3,12 3,14 @@ file [1, #0] [0:79]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [4] [10:79]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var: [28:39]
        ident [y] [32:33]
        ident [bool] [35:39]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      var [28:39]
        y: [32:39]
          ident [y] [32:33]
          ident [bool] [35:39]
      if [1] [42:68]
        item [45:46]
          ident [x] [45:46]

M pkg/parser/testdata/if_else_if_else.snow.want => pkg/parser/testdata/if_else_if_else.snow.want +8 -6
@@ 3,12 3,14 @@ file [1, #0] [0:90]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [4] [10:90]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var: [28:39]
        ident [y] [32:33]
        ident [bool] [35:39]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      var [28:39]
        y: [32:39]
          ident [y] [32:33]
          ident [bool] [35:39]
      if [1] [42:79]
        item [45:46]
          ident [x] [45:46]

M pkg/parser/testdata/if_nested.snow.want => pkg/parser/testdata/if_nested.snow.want +8 -6
@@ 3,12 3,14 @@ file [1, #0] [0:93]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [4] [10:93]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var: [28:39]
        ident [y] [32:33]
        ident [bool] [35:39]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      var [28:39]
        y: [32:39]
          ident [y] [32:33]
          ident [bool] [35:39]
      if [1] [42:82]
        item [45:46]
          ident [x] [45:46]

M pkg/parser/testdata/if_no_block.snow.want => pkg/parser/testdata/if_no_block.snow.want +4 -3
@@ 3,9 3,10 @@ file [1, #0] [0:35]
    ident [test] [3:7]
    sig [0->0] [7:9]
    block [2] [10:35]
      var: [14:25]
        ident [x] [18:19]
        ident [bool] [21:25]
      var [14:25]
        x: [18:25]
          ident [x] [18:19]
          ident [bool] [21:25]
      if [1] [28:34]
        item [31:32]
          ident [x] [31:32]

M pkg/parser/testdata/let.snow.want => pkg/parser/testdata/let.snow.want +4 -3
@@ 1,4 1,5 @@
file [1, #0] [0:10]
  let: [0:10]
    ident [x] [4:5]
    ident [int] [7:10]
  let [0:10]
    x: [4:10]
      ident [x] [4:5]
      ident [int] [7:10]

M pkg/parser/testdata/let_full.snow.want => pkg/parser/testdata/let_full.snow.want +5 -4
@@ 1,5 1,6 @@
file [1, #0] [0:21]
  let:= [0:21]
    ident [x] [4:5]
    ident [string] [7:13]
    string ["abc"] [16:21]
  let [0:21]
    x:= [4:21]
      ident [x] [4:5]
      ident [string] [7:13]
      string ["abc"] [16:21]

M pkg/parser/testdata/let_infer.snow.want => pkg/parser/testdata/let_infer.snow.want +4 -3
@@ 1,4 1,5 @@
file [1, #0] [0:11]
  let= [0:11]
    ident [x] [4:5]
    string ["a"] [8:11]
  let [0:11]
    x= [4:11]
      ident [x] [4:5]
      string ["a"] [8:11]

M pkg/parser/testdata/let_kwonly.snow.err => pkg/parser/testdata/let_kwonly.snow.err +0 -1
@@ 1,3 1,2 @@
testdata/let_kwonly.snow:1:5: expected 'ident', found 'eof'
testdata/let_kwonly.snow:1:5: expected ';', found 'eof'
testdata/let_kwonly.snow:1:1: missing variable type or initialization

M pkg/parser/testdata/let_kwonly.snow.want => pkg/parser/testdata/let_kwonly.snow.want +2 -3
@@ 1,3 1,2 @@
file [1, #0] [0:4]
  let [0:4]
    ident [] [4:4]
file [1, #0] [0:3]
  let [0:3]

M pkg/parser/testdata/let_question.snow.want => pkg/parser/testdata/let_question.snow.want +4 -3
@@ 1,4 1,5 @@
file [1, #0] [0:18]
  let: [0:18]
    ident [results?] [4:12]
    ident [bool] [14:18]
  let [0:18]
    results?: [4:18]
      ident [results?] [4:12]
      ident [bool] [14:18]

A pkg/parser/testdata/multi_args_invalid_trailing_comma.snow => pkg/parser/testdata/multi_args_invalid_trailing_comma.snow +4 -0
@@ 0,0 1,4 @@
fn main() {
  let x: int, y: int,
  x + y
}

A pkg/parser/testdata/multi_args_invalid_trailing_comma.snow.err => pkg/parser/testdata/multi_args_invalid_trailing_comma.snow.err +4 -0
@@ 0,0 1,4 @@
testdata/multi_args_invalid_trailing_comma.snow:3:3: missing variable type or initialization
testdata/multi_args_invalid_trailing_comma.snow:3:5: expected ';', found '+'
testdata/multi_args_invalid_trailing_comma.snow:4:3: expected '}', found 'eof'
testdata/multi_args_invalid_trailing_comma.snow:4:3: expected ';', found 'eof'

A pkg/parser/testdata/multi_args_invalid_trailing_comma.snow.want => pkg/parser/testdata/multi_args_invalid_trailing_comma.snow.want +14 -0
@@ 0,0 1,14 @@
file [1, #0] [0:44]
  fn [0:44]
    ident [main] [3:7]
    sig [0->0] [7:9]
    block [1] [10:44]
      let [14:37]
        x: [18:25]
          ident [x] [18:19]
          ident [int] [21:24]
        y: [26:33]
          ident [y] [26:27]
          ident [int] [29:32]
        x [36:37]
          ident [x] [36:37]

A pkg/parser/testdata/multi_args_per_decl.snow => pkg/parser/testdata/multi_args_per_decl.snow +1 -0
@@ 0,0 1,1 @@
var x: int, y = "a", z: bool = true

A pkg/parser/testdata/multi_args_per_decl.snow.err => pkg/parser/testdata/multi_args_per_decl.snow.err +0 -0
A pkg/parser/testdata/multi_args_per_decl.snow.want => pkg/parser/testdata/multi_args_per_decl.snow.want +12 -0
@@ 0,0 1,12 @@
file [1, #0] [0:35]
  var [0:35]
    x: [4:11]
      ident [x] [4:5]
      ident [int] [7:10]
    y= [12:20]
      ident [y] [12:13]
      string ["a"] [16:19]
    z:= [21:35]
      ident [z] [21:22]
      ident [bool] [24:28]
      ident [true] [31:35]

M pkg/parser/testdata/struct_decl.snow.want => pkg/parser/testdata/struct_decl.snow.want +8 -6
@@ 1,9 1,11 @@
file [1, #0] [0:42]
  struct [2] [0:42]
    ident [Point] [7:12]
    var: [17:27]
      ident [x] [21:22]
      ident [int] [24:27]
    var: [30:40]
      ident [y] [34:35]
      ident [int] [37:40]
    var [17:27]
      x: [21:27]
        ident [x] [21:22]
        ident [int] [24:27]
    var [30:40]
      y: [34:40]
        ident [y] [34:35]
        ident [int] [37:40]

M pkg/parser/testdata/var.snow.want => pkg/parser/testdata/var.snow.want +4 -3
@@ 1,4 1,5 @@
file [1, #0] [0:10]
  var: [0:10]
    ident [x] [4:5]
    ident [int] [7:10]
  var [0:10]
    x: [4:10]
      ident [x] [4:5]
      ident [int] [7:10]

M pkg/parser/testdata/var_bang.snow.want => pkg/parser/testdata/var_bang.snow.want +4 -3
@@ 1,4 1,5 @@
file [1, #0] [0:17]
  var= [0:17]
    ident [ready!] [4:10]
    ident [true] [13:17]
  var [0:17]
    ready!= [4:17]
      ident [ready!] [4:10]
      ident [true] [13:17]

M pkg/parser/testdata/var_comma_for_semi.snow.err => pkg/parser/testdata/var_comma_for_semi.snow.err +1 -1
@@ 1,1 1,1 @@
testdata/var_comma_for_semi.snow:1:11: expected ';', found ','
testdata/var_comma_for_semi.snow:1:13: expected ';', found 'eof'

M pkg/parser/testdata/var_comma_for_semi.snow.want => pkg/parser/testdata/var_comma_for_semi.snow.want +5 -4
@@ 1,4 1,5 @@
file [1, #0] [0:10]
  var: [0:10]
    ident [x] [4:5]
    ident [int] [7:10]
file [1, #0] [0:11]
  var [0:11]
    x: [4:11]
      ident [x] [4:5]
      ident [int] [7:10]

M pkg/parser/testdata/var_full.snow.want => pkg/parser/testdata/var_full.snow.want +5 -4
@@ 1,5 1,6 @@
file [1, #0] [0:16]
  var:= [0:16]
    ident [x] [4:5]
    ident [int] [7:10]
    int [123] [13:16]
  var [0:16]
    x:= [4:16]
      ident [x] [4:5]
      ident [int] [7:10]
      int [123] [13:16]

M pkg/parser/testdata/var_infer.snow.want => pkg/parser/testdata/var_infer.snow.want +4 -3
@@ 1,4 1,5 @@
file [1, #0] [0:10]
  var= [0:10]
    ident [x] [4:5]
    int [10] [8:10]
  var [0:10]
    x= [4:10]
      ident [x] [4:5]
      int [10] [8:10]

M pkg/parser/testdata/var_invalid_empty_tuple.snow.want => pkg/parser/testdata/var_invalid_empty_tuple.snow.want +4 -3
@@ 1,4 1,5 @@
file [1, #0] [0:9]
  var: [0:9]
    ident [x] [4:5]
    bad expr [7:9]
  var [0:9]
    x: [4:9]
      ident [x] [4:5]
      bad expr [7:9]

M pkg/parser/testdata/var_invalid_type.snow.want => pkg/parser/testdata/var_invalid_type.snow.want +4 -3
@@ 1,4 1,5 @@
file [1, #0] [0:7]
  var: [0:7]
    ident [x] [4:5]
    bad expr [7:7]
  var [0:7]
    x: [4:7]
      ident [x] [4:5]
      bad expr [7:7]

M pkg/parser/testdata/var_kw_as_ident.snow.want => pkg/parser/testdata/var_kw_as_ident.snow.want +112 -84
@@ 1,85 1,113 @@
file [28, #0] [0:444]
  var: [0:13]
    ident [] [4:4]
    ident [bool] [9:13]
  var: [14:29]
    ident [] [18:18]
    ident [string] [23:29]
  var: [30:40]
    ident [] [34:34]
    ident [i8] [38:40]
  var: [41:56]
    ident [] [45:45]
    ident [i16] [53:56]
  var: [57:67]
    ident [] [61:61]
    ident [u8] [65:67]
  var: [68:82]
    ident [] [72:72]
    ident [i32] [79:82]
  var: [83:96]
    ident [] [87:87]
    ident [i64] [93:96]
  var: [97:112]
    ident [] [101:101]
    ident [u16] [109:112]
  let: [114:129]
    ident [break] [118:123]
    ident [bool] [125:129]
  let: [131:143]
    ident [case] [135:139]
    ident [i8] [141:143]
  let: [145:158]
    ident [chan] [149:153]
    ident [i16] [155:158]
  let: [160:174]
    ident [const] [164:169]
    ident [i32] [171:174]
  let: [176:193]
    ident [continue] [180:188]
    ident [i64] [190:193]
  let: [195:211]
    ident [default] [199:206]
    ident [int] [208:211]
  let: [213:226]
    ident [defer] [217:222]
    ident [u8] [224:226]
  let: [228:248]
    ident [fallthrough] [232:243]
    ident [u16] [245:248]
  let: [250:262]
    ident [for] [254:257]
    ident [u32] [259:262]
  let: [264:277]
    ident [func] [268:272]
    ident [u64] [274:277]
  let: [279:291]
    ident [go] [283:285]
    ident [uint] [287:291]
  let: [293:308]
    ident [goto] [297:301]
    ident [float] [303:308]
  let: [310:325]
    ident [import] [314:320]
    ident [f32] [322:325]
  let: [327:345]
    ident [interface] [331:340]
    ident [f64] [342:345]
  let: [347:362]
    ident [map] [351:354]
    ident [string] [356:362]
  let: [364:379]
    ident [package] [368:375]
    ident [i8] [377:379]
  let: [381:395]
    ident [range] [385:390]
    ident [i16] [392:395]
  let: [397:412]
    ident [select] [401:407]
    ident [i32] [409:412]
  let: [414:429]
    ident [switch] [418:424]
    ident [i64] [426:429]
  let: [431:444]
    ident [type] [435:439]
    ident [int] [441:444]
  var [0:13]
    : [4:13]
      ident [] [4:4]
      ident [bool] [9:13]
  var [14:29]
    : [18:29]
      ident [] [18:18]
      ident [string] [23:29]
  var [30:40]
    : [34:40]
      ident [] [34:34]
      ident [i8] [38:40]
  var [41:56]
    : [45:56]
      ident [] [45:45]
      ident [i16] [53:56]
  var [57:67]
    : [61:67]
      ident [] [61:61]
      ident [u8] [65:67]
  var [68:82]
    : [72:82]
      ident [] [72:72]
      ident [i32] [79:82]
  var [83:96]
    : [87:96]
      ident [] [87:87]
      ident [i64] [93:96]
  var [97:112]
    : [101:112]
      ident [] [101:101]
      ident [u16] [109:112]
  let [114:129]
    break: [118:129]
      ident [break] [118:123]
      ident [bool] [125:129]
  let [131:143]
    case: [135:143]
      ident [case] [135:139]
      ident [i8] [141:143]
  let [145:158]
    chan: [149:158]
      ident [chan] [149:153]
      ident [i16] [155:158]
  let [160:174]
    const: [164:174]
      ident [const] [164:169]
      ident [i32] [171:174]
  let [176:193]
    continue: [180:193]
      ident [continue] [180:188]
      ident [i64] [190:193]
  let [195:211]
    default: [199:211]
      ident [default] [199:206]
      ident [int] [208:211]
  let [213:226]
    defer: [217:226]
      ident [defer] [217:222]
      ident [u8] [224:226]
  let [228:248]
    fallthrough: [232:248]
      ident [fallthrough] [232:243]
      ident [u16] [245:248]
  let [250:262]
    for: [254:262]
      ident [for] [254:257]
      ident [u32] [259:262]
  let [264:277]
    func: [268:277]
      ident [func] [268:272]
      ident [u64] [274:277]
  let [279:291]
    go: [283:291]
      ident [go] [283:285]
      ident [uint] [287:291]
  let [293:308]
    goto: [297:308]
      ident [goto] [297:301]
      ident [float] [303:308]
  let [310:325]
    import: [314:325]
      ident [import] [314:320]
      ident [f32] [322:325]
  let [327:345]
    interface: [331:345]
      ident [interface] [331:340]
      ident [f64] [342:345]
  let [347:362]
    map: [351:362]
      ident [map] [351:354]
      ident [string] [356:362]
  let [364:379]
    package: [368:379]
      ident [package] [368:375]
      ident [i8] [377:379]
  let [381:395]
    range: [385:395]
      ident [range] [385:390]
      ident [i16] [392:395]
  let [397:412]
    select: [401:412]
      ident [select] [401:407]
      ident [i32] [409:412]
  let [414:429]
    switch: [418:429]
      ident [switch] [418:424]
      ident [i64] [426:429]
  let [431:444]
    type: [435:444]
      ident [type] [435:439]
      ident [int] [441:444]

M pkg/parser/testdata/var_kwonly.snow.err => pkg/parser/testdata/var_kwonly.snow.err +0 -1
@@ 1,3 1,2 @@
testdata/var_kwonly.snow:1:5: expected 'ident', found 'eof'
testdata/var_kwonly.snow:1:5: expected ';', found 'eof'
testdata/var_kwonly.snow:1:1: missing variable type or initialization

M pkg/parser/testdata/var_kwonly.snow.want => pkg/parser/testdata/var_kwonly.snow.want +2 -3
@@ 1,3 1,2 @@
file [1, #0] [0:4]
  var [0:4]
    ident [] [4:4]
file [1, #0] [0:3]
  var [0:3]

M pkg/parser/testdata/var_missing_type_init.snow.err => pkg/parser/testdata/var_missing_type_init.snow.err +1 -1
@@ 1,1 1,1 @@
testdata/var_missing_type_init.snow:1:1: missing variable type or initialization
testdata/var_missing_type_init.snow:1:5: missing variable type or initialization

M pkg/parser/testdata/var_missing_type_init.snow.want => pkg/parser/testdata/var_missing_type_init.snow.want +2 -1
@@ 1,3 1,4 @@
file [1, #0] [0:5]
  var [0:5]
    ident [x] [4:5]
    x [4:5]
      ident [x] [4:5]

M pkg/parser/testdata/var_op_precedence.snow.want => pkg/parser/testdata/var_op_precedence.snow.want +22 -21
@@ 1,22 1,23 @@
file [1, #0] [0:43]
  var= [0:43]
    ident [x] [4:5]
    binary [-] [8:43]
      binary [+] [8:34]
        binary [-] [8:29]
          binary [+] [8:17]
            int [1] [8:9]
            binary [*] [12:17]
              int [2] [12:13]
              int [3] [16:17]
          binary [%] [20:29]
            binary [/] [20:25]
              int [4] [20:21]
              int [5] [24:25]
            int [6] [28:29]
        unary [-] [32:34]
          int [7] [33:34]
      binary [*] [37:43]
        unary [+] [37:39]
          int [8] [38:39]
        int [9] [42:43]
  var [0:43]
    x= [4:43]
      ident [x] [4:5]
      binary [-] [8:43]
        binary [+] [8:34]
          binary [-] [8:29]
            binary [+] [8:17]
              int [1] [8:9]
              binary [*] [12:17]
                int [2] [12:13]
                int [3] [16:17]
            binary [%] [20:29]
              binary [/] [20:25]
                int [4] [20:21]
                int [5] [24:25]
              int [6] [28:29]
          unary [-] [32:34]
            int [7] [33:34]
        binary [*] [37:43]
          unary [+] [37:39]
            int [8] [38:39]
          int [9] [42:43]

M pkg/parser/testdata/var_paren_type.snow.want => pkg/parser/testdata/var_paren_type.snow.want +5 -4
@@ 1,5 1,6 @@
file [1, #0] [0:12]
  var: [0:12]
    ident [x] [4:5]
    paren [7:12]
      ident [int] [8:11]
  var [0:12]
    x: [4:12]
      ident [x] [4:5]
      paren [7:12]
        ident [int] [8:11]

M pkg/parser/testdata/var_paren_type_trail_comma.snow.want => pkg/parser/testdata/var_paren_type_trail_comma.snow.want +5 -4
@@ 1,5 1,6 @@
file [1, #0] [0:14]
  var: [0:14]
    ident [x] [4:5]
    paren [7:14]
      ident [int] [8:11]
  var [0:14]
    x: [4:14]
      ident [x] [4:5]
      paren [7:14]
        ident [int] [8:11]

M pkg/parser/testdata/var_tuple.snow.want => pkg/parser/testdata/var_tuple.snow.want +10 -9
@@ 1,10 1,11 @@
file [1, #0] [0:23]
  var= [0:23]
    ident [t] [4:5]
    tuple value [3] [8:23]
      item [9:11]
        int [1] [9:10]
      item [12:16]
        string ["a"] [12:15]
      item [17:22]
        ident [true] [17:21]
  var [0:23]
    t= [4:23]
      ident [t] [4:5]
      tuple value [3] [8:23]
        item [9:11]
          int [1] [9:10]
        item [12:16]
          string ["a"] [12:15]
        item [17:22]
          ident [true] [17:21]

M pkg/parser/testdata/var_tuple_type.snow.want => pkg/parser/testdata/var_tuple_type.snow.want +8 -7
@@ 1,8 1,9 @@
file [1, #0] [0:19]
  var: [0:19]
    ident [x] [4:5]
    tuple type [2] [7:19]
      param [8:12]
        ident [int] [8:11]
      param [13:18]
        ident [uint] [13:17]
  var [0:19]
    x: [4:19]
      ident [x] [4:5]
      tuple type [2] [7:19]
        param [8:12]
          ident [int] [8:11]
        param [13:18]
          ident [uint] [13:17]

M pkg/printer/printer.go => pkg/printer/printer.go +3 -1
@@ 296,7 296,9 @@ func (p *astPrinter) Visit(n ast.Node) ast.Visitor {
	case *ast.KeyValue:
		p.printMsg("field", true)
	case *ast.VarDecl:
		lbl := n.Kw.String()
		p.printMsg(n.Kw.String(), true)
	case *ast.VarDef:
		lbl := n.Name.Name
		if n.Colon.IsValid() {
			lbl += ":"
		}

M pkg/printer/printer_test.go => pkg/printer/printer_test.go +38 -30
@@ 28,20 28,24 @@ func TestPrinter(t *testing.T) {
			&ast.VarDecl{
				Kw:    token.Var,
				KwPos: 9,
				Name: &ast.Ident{
					Name:     "a",
					IdentPos: 13,
				},
				Colon: 14,
				Type: &ast.Ident{
					Name:     "int",
					IdentPos: 16,
				},
				Assign: 20,
				Value: &ast.BasicLit{
					LitPos:  22,
					Literal: token.Int,
					Value:   "1",
				Vars: []*ast.VarDef{
					{
						Name: &ast.Ident{
							Name:     "a",
							IdentPos: 13,
						},
						Colon: 14,
						Type: &ast.Ident{
							Name:     "int",
							IdentPos: 16,
						},
						Assign: 20,
						Value: &ast.BasicLit{
							LitPos:  22,
							Literal: token.Int,
							Value:   "1",
						},
					},
				},
			},



@@ 112,10 116,11 @@ func TestPrinter(t *testing.T) {

	wantNoPos := `
file [2, #1]
    var:=
      ident [a]
      ident [int]
      int [1]
    var
      a:=
        ident [a]
        ident [int]
        int [1]
    fn
      ident [add]
      sig [2->1]


@@ 137,10 142,11 @@ file [2, #1]
  comments [2]
    comment [#abc]
    comment [#def]
  var:=
    ident [a]
    ident [int]
    int [1]
  var
    a:=
      ident [a]
      ident [int]
      int [1]
  fn
    ident [add]
    sig [2->1]


@@ 159,10 165,11 @@ file [2, #1]
`
	wantShort := `
file [2, #1] [0:69]
    var:= [8:22]
      ident [a] [12:13]
      ident [int] [15:18]
      int [1] [21:22]
    var [8:22]
      a:= [12:22]
        ident [a] [12:13]
        ident [int] [15:18]
        int [1] [21:22]
    fn [23:69]
      ident [add] [26:29]
      sig [2->1] [30:51]


@@ 181,10 188,11 @@ file [2, #1] [0:69]
`
	wantLong := `
file [2, #1] [test:1:1::1:70]
    var:= [test:1:9::1:23]
      ident [a] [test:1:13::1:14]
      ident [int] [test:1:16::1:19]
      int [1] [test:1:22::1:23]
    var [test:1:9::1:23]
      a:= [test:1:13::1:23]
        ident [a] [test:1:13::1:14]
        ident [int] [test:1:16::1:19]
        int [1] [test:1:22::1:23]
    fn [test:1:24::1:70]
      ident [add] [test:1:27::1:30]
      sig [2->1] [test:1:31::1:52]

A pkg/semantic/testdata/check/fn_multi_vars_per_decl.snow.err => pkg/semantic/testdata/check/fn_multi_vars_per_decl.snow.err +0 -0
A pkg/semantic/testdata/check/fn_multi_vars_per_decl.snow.want => pkg/semantic/testdata/check/fn_multi_vars_per_decl.snow.want +15 -0
@@ 0,0 1,15 @@
file testdata/fn_multi_vars_per_decl.snow [0, 1, 0]
  fn main [let: () -> void]
    block [3]
      var:= x [var: int]
        ident int [type: int]
        int [1] [const: int]
      var:= y [var: i64]
        ident i64 [type: i64]
        implicit conv [value: i64]
          int [2] [const: int]
      expr
        binary [+] [value: i64]
          implicit conv [value: i64]
            ident x [var: int]
          ident y [var: i64]

A pkg/semantic/testdata/fn_multi_vars_per_decl.snow => pkg/semantic/testdata/fn_multi_vars_per_decl.snow +4 -0
@@ 0,0 1,4 @@
fn main() {
  var x: int = 1, y: i64 = 2
  x + y
}

A pkg/semantic/testdata/scopes/fn_multi_vars_per_decl.snow.err => pkg/semantic/testdata/scopes/fn_multi_vars_per_decl.snow.err +0 -0
A pkg/semantic/testdata/scopes/fn_multi_vars_per_decl.snow.want => pkg/semantic/testdata/scopes/fn_multi_vars_per_decl.snow.want +33 -0
@@ 0,0 1,33 @@
1 *semantic.Unit {
.  bool
.  extern
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  2 *semantic.Struct {
.  .  import
.  .  pkg
.  .  symbol
.  }
.  3 *semantic.File {
.  .  main
.  .  4 *semantic.Fn {
.  .  .  x
.  .  .  y
.  .  }
.  }
}

A pkg/semantic/testdata/static/fn_multi_vars_per_decl.snow.err => pkg/semantic/testdata/static/fn_multi_vars_per_decl.snow.err +0 -0
A pkg/semantic/testdata/static/fn_multi_vars_per_decl.snow.want => pkg/semantic/testdata/static/fn_multi_vars_per_decl.snow.want +2 -0
@@ 0,0 1,2 @@
testdata/fn_multi_vars_per_decl.snow:3:3: x
testdata/fn_multi_vars_per_decl.snow:3:7: y

A pkg/semantic/testdata/types/fn_multi_vars_per_decl.snow.err => pkg/semantic/testdata/types/fn_multi_vars_per_decl.snow.err +0 -0
A pkg/semantic/testdata/types/fn_multi_vars_per_decl.snow.want => pkg/semantic/testdata/types/fn_multi_vars_per_decl.snow.want +13 -0
@@ 0,0 1,13 @@
file testdata/fn_multi_vars_per_decl.snow [0, 1, 0]
  fn main [let: () -> void]
    block [3]
      var:= x [var: int]
        ident int [type: int]
        int [1] [const: int]
      var:= y [var: i64]
        ident i64 [type: i64]
        int [2] [const: int]
      expr
        binary [+] [value: i64]
          ident x [var: int]
          ident y [var: i64]

M pkg/semantic/translate_pass.go => pkg/semantic/translate_pass.go +31 -16
@@ 37,7 37,7 @@ type translateVisitor struct {
	errh              func(token.Pos, string)
	scope             *Scope
	preventBlockScope bool
	generated         Node
	generated         interface{}
}

func (t *translateVisitor) clone(s *Scope) *translateVisitor {


@@ 68,8 68,8 @@ func (t *translateVisitor) Visit(n ast.Node) ast.Visitor {
		for _, decl := range n.Decls {
			ast.Walk(tt, decl)
			switch gn := tt.generated.(type) {
			case *Var:
				f.Vars = append(f.Vars, gn)
			case []*Var:
				f.Vars = append(f.Vars, gn...)
			case *Fn:
				f.Fns = append(f.Fns, gn)
			case *Struct:


@@ 126,19 126,25 @@ func (t *translateVisitor) Visit(n ast.Node) ast.Visitor {
		t.generated = &fn

	case *ast.VarDecl:
		v := t.buildVarOrExpr(n.Kw, n.Name, n.Type).(*Var)
		v.Ctx = Immutable
		ctx := Immutable
		if n.Kw == token.Var {
			v.Ctx = Mutable
			ctx = Mutable
		}
		if n.Value != nil {
			ast.Walk(t, n.Value)
			v.Value = t.generated.(Expr)
		}
		if str, ok := v.scope.Owner.(*Struct); ok {
			v.PropOf = str

		list := make([]*Var, len(n.Vars))
		for i, vd := range n.Vars {
			v := t.buildVarOrExpr(n.Kw, vd.Name, vd.Type).(*Var)
			v.Ctx = ctx
			if vd.Value != nil {
				ast.Walk(t, vd.Value)
				v.Value = t.generated.(Expr)
			}
			if str, ok := v.scope.Owner.(*Struct); ok {
				v.PropOf = str
			}
			list[i] = v
		}
		t.generated = v
		t.generated = list

	case *ast.StructDecl:
		var str Struct


@@ 153,8 159,8 @@ func (t *translateVisitor) Visit(n ast.Node) ast.Visitor {
		for _, decl := range n.Decls {
			ast.Walk(tt, decl)
			switch gn := tt.generated.(type) {
			case *Var:
				str.Vars = append(str.Vars, gn)
			case []*Var:
				str.Vars = append(str.Vars, gn...)
			case *Fn:
				str.Fns = append(str.Fns, gn)
			case *Struct:


@@ 228,7 234,16 @@ func (t *translateVisitor) Visit(n ast.Node) ast.Visitor {
		}
		for _, stmt := range n.Stmts {
			ast.Walk(tt, stmt)
			b.Stmts = append(b.Stmts, tt.generated.(Stmt))
			switch nn := tt.generated.(type) {
			case Stmt:
				b.Stmts = append(b.Stmts, nn)
			case []*Var:
				for _, stmt := range nn {
					b.Stmts = append(b.Stmts, stmt)
				}
			default:
				panic(fmt.Sprintf("invalid generated block node: %T", nn))
			}
		}
		t.generated = &b