<commit_before>package alpha

import (
	"fmt"
	"github.com/rhysd/gocaml/ast"
)

// Alpha transform.
// Transform all idenfiers to unique ones.
//
// Example:
//   Before: let x = 1 in let x = 2 in x + x
//   After:  let x$1 = 1 in let x$2 = 2 in x$2 + x$2

func duplicateSymbol(symbols []*ast.Symbol) *ast.Symbol {
	len := len(symbols)
	for i, left := range symbols {
		for _, right := range symbols[i+1 : len] {
			if left.Name == right.Name {
				return left
			}
		}
	}
	return nil
}

type transformer struct {
	current *mapping
	count   uint
	err     error
}

func newTransformer() *transformer {
	return &transformer{
		current: newMapping(nil),
		count:   0,
		err:     nil,
	}
}

func (t *transformer) setDuplicateError(node ast.Expr, name string) {
	pos := node.Pos()
	t.err = fmt.Errorf("Detected duplicate symbol '%s' in node '%s' at (line:%d, column:%d)", name, node.Name(), pos.Line, pos.Column)
}

func (t *transformer) newID(n string) string {
	t.count++
	return fmt.Sprintf("%s$t%d", n, t.count)
}

func (t *transformer) register(s *ast.Symbol) {
	mapped := t.newID(s.Name)
	t.current.vars[s.Name] = mapped
	s.ID = mapped
}

func (t *transformer) nest() {
	t.current = newMapping(t.current)
}

func (t *transformer) pop() {
	t.current = t.current.parent
}

func (t *transformer) Visit(node ast.Expr) ast.Visitor {
	switch n := node.(type) {
	case *ast.Let:
		// At first, transform value bound to the variable
		ast.Visit(t, n.Bound)
		t.nest()
		t.register(n.Symbol)
		ast.Visit(t, n.Body)
		t.pop()
		return nil
	case *ast.LetRec:
		if s := duplicateSymbol(n.Func.Params); s != nil {
			t.setDuplicateError(n, s.Name)
			return nil
		}
		t.nest()
		for _, p := range n.Func.Params {
			t.register(p)
		}
		ast.Visit(t, n.Func.Body)
		t.register(n.Func.Symbol)
		ast.Visit(t, n.Body)
		t.pop()
		return nil
	case *ast.LetTuple:
		ast.Visit(t, n.Bound)
		if s := duplicateSymbol(n.Symbols); s != nil {
			t.setDuplicateError(n, s.Name)
			return nil
		}
		t.nest()
		for _, e := range n.Symbols {
			t.register(e)
		}
		ast.Visit(t, n.Body)
		t.pop()
		return nil
	case *ast.VarRef:
		mapped, ok := t.current.resolve(n.Ident)
		if !ok {
			// External symbol is ignored because name should be identical.
			return nil
		}
		n.Ident = mapped
		return nil
	}

	// Visit recursively
	return t
}

func Transform(root ast.Expr) error {
	v := newTransformer()
	ast.Visit(v, root)
	return v.err
}
<commit_msg>fix alpha transform in recursive call case<commit_after>package alpha

import (
	"fmt"
	"github.com/rhysd/gocaml/ast"
)

// Alpha transform.
// Transform all idenfiers to unique ones.
//
// Example:
//   Before: let x = 1 in let x = 2 in x + x
//   After:  let x$1 = 1 in let x$2 = 2 in x$2 + x$2

func duplicateSymbol(symbols []*ast.Symbol) *ast.Symbol {
	len := len(symbols)
	for i, left := range symbols {
		for _, right := range symbols[i+1 : len] {
			if left.Name == right.Name {
				return left
			}
		}
	}
	return nil
}

type transformer struct {
	current *mapping
	count   uint
	err     error
}

func newTransformer() *transformer {
	return &transformer{
		current: newMapping(nil),
		count:   0,
		err:     nil,
	}
}

func (t *transformer) setDuplicateError(node ast.Expr, name string) {
	pos := node.Pos()
	t.err = fmt.Errorf("Detected duplicate symbol '%s' in node '%s' at (line:%d, column:%d)", name, node.Name(), pos.Line, pos.Column)
}

func (t *transformer) newID(n string) string {
	t.count++
	return fmt.Sprintf("%s$t%d", n, t.count)
}

func (t *transformer) register(s *ast.Symbol) {
	mapped := t.newID(s.Name)
	t.current.vars[s.Name] = mapped
	s.ID = mapped
}

func (t *transformer) nest() {
	t.current = newMapping(t.current)
}

func (t *transformer) pop() {
	t.current = t.current.parent
}

func (t *transformer) Visit(node ast.Expr) ast.Visitor {
	switch n := node.(type) {
	case *ast.Let:
		// At first, transform value bound to the variable
		ast.Visit(t, n.Bound)
		t.nest()
		t.register(n.Symbol)
		ast.Visit(t, n.Body)
		t.pop()
		return nil
	case *ast.LetRec:
		if s := duplicateSymbol(n.Func.Params); s != nil {
			t.setDuplicateError(n, s.Name)
			return nil
		}
		t.nest()
		for _, p := range n.Func.Params {
			t.register(p)
		}
		t.register(n.Func.Symbol)
		ast.Visit(t, n.Func.Body)
		ast.Visit(t, n.Body)
		t.pop()
		return nil
	case *ast.LetTuple:
		ast.Visit(t, n.Bound)
		if s := duplicateSymbol(n.Symbols); s != nil {
			t.setDuplicateError(n, s.Name)
			return nil
		}
		t.nest()
		for _, e := range n.Symbols {
			t.register(e)
		}
		ast.Visit(t, n.Body)
		t.pop()
		return nil
	case *ast.VarRef:
		mapped, ok := t.current.resolve(n.Ident)
		if !ok {
			// External symbol is ignored because name should be identical.
			return nil
		}
		n.Ident = mapped
		return nil
	}

	// Visit recursively
	return t
}

func Transform(root ast.Expr) error {
	v := newTransformer()
	ast.Visit(v, root)
	return v.err
}

{'fraction_non_alphanumeric': 0.15381451778737404, 'fraction_numerical': 0.004112687641373638, 'mean_word_length': 3.0365145228215766, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '611148', 'n_tokens_mistral': 2056, 'n_tokens_neox': 1896, 'n_words': 506}