> For the complete documentation index, see [llms.txt](https://gofast.disasm.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gofast.disasm.dev/examples/unroll-sequence-expression.md).

# Unroll Sequence Expression

The `Unroll2Visitor` walks the AST and rewrites statements that use sequence expressions (`(x, y, z)`) into multiple distinct statements.

#### 🛠️ What It Does

This visitor finds sequence expressions inside various constructs and transforms:

**🧾 Examples:**

* `return (x, y, z);` →

```javascript
x;
y;
return z;
```

* `if (x, y, z) {}` →

```javascript
x;
y;
if (z) {}
```

* `w = (x, y, z);` →

```javascript
x;
y;
w = z;
```

````go
package main

import "github.com/t14raptor/go-fast/ast"

type Unroll2Visitor struct {
	ast.NoopVisitor

	stmts *ast.Statements
	index int
}

func (v *Unroll2Visitor) insert(n int, seq ast.Expressions, trimLast bool) {
	if trimLast {
		// Trim the last statement of the slice if needed.
		n--
		seq = seq[:len(seq)-1]
	}

	// Create a larger slice of statements to insert the expressions.
	newStmts := make(ast.Statements, len(*v.stmts)+n)

	// Copy over the old statements but have room to insert the expressions later.
	// Note: This is all equivalent to slices.Insert(*v.stmts, v.index, seqStmts...),
	// but we do this instead to reduce heap allocations.
	copy(newStmts[:v.index], (*v.stmts)[:v.index])
	copy(newStmts[v.index+n:], (*v.stmts)[v.index:])

	// Insert expressions as expression statements into the new slice.
	for i := range seq {
		newStmts[v.index+i].Stmt = &ast.ExpressionStatement{Expression: &seq[i]}
	}

	// Shift the index to account for the recently inserted expressions from sequences.
	v.index += n
	
	*v.stmts = newStmts
}

func (v *Unroll2Visitor) VisitStatements(n *ast.Statements) {
	parent, parentIndex := v.stmts, v.index

	// Track the current statements and the current index to know where to insert
	// statements for unrolling.
	v.stmts = n
	for v.index = 0; v.index < len(*v.stmts); v.index++ {
		(*v.stmts)[v.index].VisitWith(v)
	}

	v.stmts, v.index = parent, parentIndex
}

func (v *Unroll2Visitor) VisitExpressionStatement(n *ast.ExpressionStatement) {
	n.VisitChildrenWith(v)

	switch expr := n.Expression.Expr.(type) {
	// This case unrolls basic sequence expressions.
	// Input:
	// ```js
	// (x, y, z);
	// ```
	// Output:
	// ```js
	// x;
	// y;
	// z;
	// ```
	case *ast.SequenceExpression:
		v.insert(len(expr.Sequence)-1, expr.Sequence, false)
	// This case unrolls sequence expressions inside of assign expressions.
	// Input:
	// ```js
	// w = (x, y, z);
	// ```
	// Output:
	// ```js
	// x;
	// y;
	// w = z;
	// ```	
	case *ast.AssignExpression:
		if seq, ok := expr.Right.Expr.(*ast.SequenceExpression); ok {
			expr.Right = &seq.Sequence[len(seq.Sequence)-1]

			v.insert(len(seq.Sequence), seq.Sequence, true)
		}
	}
}

// VisitThrowStatement unrolls sequence expressions inside of throw statements.
// Input:
// ```js
// throw (x, y, z);
// ```
// Output:
// ```js
// x;
// y;
// throw z;
// ```
func (v *Unroll2Visitor) VisitThrowStatement(n *ast.ThrowStatement) {
	n.VisitChildrenWith(v)

	if seq, ok := n.Argument.Expr.(*ast.SequenceExpression); ok {
		n.Argument = &seq.Sequence[len(seq.Sequence)-1]

		v.insert(len(seq.Sequence), seq.Sequence, true)
	}
}

// VisitSwitchStatement unrolls sequence expressions inside of switch statements.
// Input:
// ```js
// switch ((x, y, z)) {}
// ```
// Output:
// ```js
// x;
// y;
// switch (z) {}
// ```
func (v *Unroll2Visitor) VisitSwitchStatement(n *ast.SwitchStatement) {
	n.VisitChildrenWith(v)

	if seq, ok := n.Discriminant.Expr.(*ast.SequenceExpression); ok {
		n.Discriminant = &seq.Sequence[len(seq.Sequence)-1]

		v.insert(len(seq.Sequence), seq.Sequence, true)
	}
}

// VisitReturnStatement unrolls sequence expressions inside of return statements.
// Input:
// ```js
// return (x, y, z);
// ```
// Output:
// ```js
// x;
// y;
// return z;
// ```
func (v *Unroll2Visitor) VisitReturnStatement(n *ast.ReturnStatement) {
	n.VisitChildrenWith(v)
	if n.Argument == nil {
		return
	}

	if seq, ok := n.Argument.Expr.(*ast.SequenceExpression); ok {
		n.Argument = &seq.Sequence[len(seq.Sequence)-1]

		v.insert(len(seq.Sequence), seq.Sequence, true)
	}
}

// VisitIfStatement unrolls sequence expressions inside of if statements.
// Input:
// ```js
// if (x, y, z) {}
// ```
// Output:
// ```js
// x;
// y;
// if (z) {}
// ```
func (v *Unroll2Visitor) VisitIfStatement(n *ast.IfStatement) {
	n.VisitChildrenWith(v)

	if seq, ok := n.Test.Expr.(*ast.SequenceExpression); ok {
		n.Test = &seq.Sequence[len(seq.Sequence)-1]

		v.insert(len(seq.Sequence), seq.Sequence, true)
	}
}

// VisitForStatement unrolls sequence expressions inside of for statements.
// Input:
// ```js
// for ((x, y, z); ; ) {}
// ```
// Output:
// ```js
// x;
// y;
// for (z; ; ) {}
// ```
func (v *Unroll2Visitor) VisitForStatement(n *ast.ForStatement) {
	n.VisitChildrenWith(v)
	if n.Initializer == nil {
		return
	}

	if forLoopInitExpr, ok := n.Initializer.Initializer.(*ast.Expression); ok {
		if seq, ok := forLoopInitExpr.Expr.(*ast.SequenceExpression); ok {
			forLoopInitExpr.Expr = &seq.Sequence[len(seq.Sequence)-1]

			v.insert(len(seq.Sequence), seq.Sequence, true)
		}
	}
}
````

#### 🧪 How to Use

```go
prog, _ := parser.ParseFile(src)
visitor := &Unroll2Visitor{}
visitor.V = visitor

prog.VisitWith(visitor)
out := generator.Generate(prog)
fmt.Println(out)
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://gofast.disasm.dev/examples/unroll-sequence-expression.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
