~mna/snow

893bd955fa7123b621de564a28f780ce326f0f77 — Martin Angers 1 year, 10 months ago 2bc035c
pkg/{ast,parser,printer}: support parsing if and guard statements
M pkg/ast/ast.go => pkg/ast/ast.go +2 -3
@@ 85,8 85,7 @@ type (
	GuardStmt struct {
		Guard token.Pos
		Conds []*ExprItem // at least one
		Else  token.Pos
		Body  *Block
		Else  *ElseClause
	}

	// BadStmt represents a bad (invalid) statement.


@@ 368,7 367,7 @@ func (i *IfStmt) End() token.Pos {
}

func (g *GuardStmt) Pos() token.Pos { return g.Guard }
func (g *GuardStmt) End() token.Pos { return g.Body.End() }
func (g *GuardStmt) End() token.Pos { return g.Else.End() }

func (e *ElseClause) Pos() token.Pos { return e.Else }
func (e *ElseClause) End() token.Pos { return e.Stmt.End() }

M pkg/ast/visitor.go => pkg/ast/visitor.go +24 -0
@@ 131,6 131,30 @@ func Walk(v Visitor, node Node) {
			Walk(v, n.Value)
		}

	case *IfStmt:
		for _, cond := range n.Conds {
			Walk(v, cond)
		}
		if n.Body != nil {
			Walk(v, n.Body)
		}
		if n.Else != nil {
			Walk(v, n.Else)
		}

	case *GuardStmt:
		for _, cond := range n.Conds {
			Walk(v, cond)
		}
		if n.Else != nil {
			Walk(v, n.Else)
		}

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

	case *CommentGroup:
		for _, comment := range n.List {
			Walk(v, comment)

M pkg/parser/parser.go => pkg/parser/parser.go +49 -7
@@ 96,7 96,7 @@ func (p *parser) parseStmt(topLevel bool) ast.Stmt {
			p.expectSemi()
			return bk
		case token.If:
			return p.parseIfStmt()
			return p.parseIfStmt(true)
		case token.Guard:
			return p.parseGuardStmt()



@@ 169,12 169,41 @@ func (p *parser) parseReturnStmt() *ast.ReturnStmt {
	return rs
}

func (p *parser) parseIfStmt() *ast.IfStmt {
	panic("not implemented")
func (p *parser) parseIfStmt(wantSemi bool) *ast.IfStmt {
	is := &ast.IfStmt{
		If: p.expect(token.If),
	}
	is.Conds = p.parseExprList(token.Lbrace)
	is.Body = p.parseBlock()
	if p.tok == token.Else {
		is.Else = p.parseElseClause(true)
	}
	if wantSemi {
		p.expectSemi()
	}
	return is
}

func (p *parser) parseGuardStmt() *ast.GuardStmt {
	panic("not implemented")
	gs := &ast.GuardStmt{
		Guard: p.expect(token.Guard),
	}
	gs.Conds = p.parseExprList(token.Else, token.Lbrace)
	gs.Else = p.parseElseClause(false)
	p.expectSemi()
	return gs
}

func (p *parser) parseElseClause(allowIf bool) *ast.ElseClause {
	ec := &ast.ElseClause{
		Else: p.expect(token.Else),
	}
	if allowIf && p.tok == token.If {
		ec.Stmt = p.parseIfStmt(false)
		return ec
	}
	ec.Stmt = p.parseBlock()
	return ec
}

func (p *parser) parseVarDef() *ast.VarDef {


@@ 285,16 314,20 @@ func (p *parser) parseCompound() ast.Expr {
			Left:   expr,
			Lparen: p.expect(token.Lparen),
		}
		call.Args = p.parseExprList()
		call.Args = p.parseExprList(token.Rparen)
		call.Rparen = p.expect(token.Rparen)
		expr = call
	}
	return expr
}

func (p *parser) parseExprList() []*ast.ExprItem {
func (p *parser) parseExprList(stop ...token.Token) []*ast.ExprItem {
	var list []*ast.ExprItem
	for p.tok != token.Rparen && p.tok != token.EOF {
	for p.tok != token.EOF {
		if tokenIn(p.tok, stop...) {
			break
		}

		ei := &ast.ExprItem{
			Expr: p.parseExpr(),
		}


@@ 488,6 521,15 @@ func (p *parser) expect(toks ...token.Token) token.Pos {
	return pos
}

func tokenIn(t token.Token, toks ...token.Token) bool {
	for _, tok := range toks {
		if t == tok {
			return true
		}
	}
	return false
}

func (p *parser) expectSemi() {
	// semicolon is optional before a closing ')' or '}'
	if p.tok != token.Rparen && p.tok != token.Rbrace {

A pkg/parser/testdata/if.snow => pkg/parser/testdata/if.snow +6 -0
@@ 0,0 1,6 @@
fn test() {
  var x: bool
  if x {
  }
  return
}

A pkg/parser/testdata/if.snow.err => pkg/parser/testdata/if.snow.err +0 -0
A pkg/parser/testdata/if.snow.want => pkg/parser/testdata/if.snow.want +13 -0
@@ 0,0 1,13 @@
file [1, #0] [0:49]
  fn [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]
      if [1] [28:38]
        item [31:32]
          ident [x] [31:32]
        block [0] [33:38]
      return [41:47]

A pkg/parser/testdata/if_else.snow => pkg/parser/testdata/if_else.snow +7 -0
@@ 0,0 1,7 @@
fn test() {
  var x: bool
  if x {
  } else {
  }
  return
}

A pkg/parser/testdata/if_else.snow.err => pkg/parser/testdata/if_else.snow.err +0 -0
A pkg/parser/testdata/if_else.snow.want => pkg/parser/testdata/if_else.snow.want +15 -0
@@ 0,0 1,15 @@
file [1, #0] [0:60]
  fn [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]
      if [1] [28:49]
        item [31:32]
          ident [x] [31:32]
        block [0] [33:38]
        else [39:49]
          block [0] [44:49]
      return [52:58]

A pkg/parser/testdata/if_else_if.snow => pkg/parser/testdata/if_else_if.snow +8 -0
@@ 0,0 1,8 @@
fn test() {
  var x: bool
  var y: bool
  if x {
  } else if y {
  }
  return
}

A pkg/parser/testdata/if_else_if.snow.err => pkg/parser/testdata/if_else_if.snow.err +0 -0
A pkg/parser/testdata/if_else_if.snow.want => pkg/parser/testdata/if_else_if.snow.want +21 -0
@@ 0,0 1,21 @@
file [1, #0] [0:79]
  fn [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]
      if [1] [42:68]
        item [45:46]
          ident [x] [45:46]
        block [0] [47:52]
        else [53:68]
          if [1] [58:68]
            item [61:62]
              ident [y] [61:62]
            block [0] [63:68]
      return [71:77]

A pkg/parser/testdata/if_else_if_else.snow => pkg/parser/testdata/if_else_if_else.snow +9 -0
@@ 0,0 1,9 @@
fn test() {
  var x: bool
  var y: bool
  if x {
  } else if y {
  } else {
  }
  return
}

A pkg/parser/testdata/if_else_if_else.snow.err => pkg/parser/testdata/if_else_if_else.snow.err +0 -0
A pkg/parser/testdata/if_else_if_else.snow.want => pkg/parser/testdata/if_else_if_else.snow.want +23 -0
@@ 0,0 1,23 @@
file [1, #0] [0:90]
  fn [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]
      if [1] [42:79]
        item [45:46]
          ident [x] [45:46]
        block [0] [47:52]
        else [53:79]
          if [1] [58:79]
            item [61:62]
              ident [y] [61:62]
            block [0] [63:68]
            else [69:79]
              block [0] [74:79]
      return [82:88]

M pkg/printer/printer.go => pkg/printer/printer.go +6 -0
@@ 165,6 165,12 @@ func (p *Printer) Visit(n ast.Node) ast.Visitor {
		p.printMsg("assign", true)
	case *ast.ExprStmt:
		p.printMsg("expr", true)
	case *ast.IfStmt:
		p.printMsg(fmt.Sprintf("if [%d]", len(n.Conds)), true)
	case *ast.GuardStmt:
		p.printMsg(fmt.Sprintf("guard [%d]", len(n.Conds)), true)
	case *ast.ElseClause:
		p.printMsg("else", true)
	case *ast.BasicLit:
		p.printMsg(fmt.Sprintf("%s [%s]", n.Literal, n.Value), true)
	case *ast.Ident: