<commit_before>package tags

import (
	"errors"
	"fmt"
	"github.com/karlseguin/liquid/core"
	"io"
	"reflect"
)

var (
	endFor = &End{"for"}
)

func ForFactory(p *core.Parser, config *core.Configuration) (core.Tag, error) {
	start := p.Position
	name := p.ReadName()
	if len(name) == 0 {
		return nil, p.Error("Invalid variable name in for tag", start)
	}
	if p.SkipSpaces() != 'i' || p.Left() < 3 || p.Data[p.Position+1] != 'n' || !core.IsTokenEnd(p.Data[p.Position+2]) {
		return nil, p.Error("Expecting keyword 'in' after variable name in for tag", start)
	}
	p.ForwardBy(2)

	value, err := p.ReadValue()
	if err != nil {
		return nil, err
	}

	p.SkipPastTag()
	return &For{
		NewCommon(),
		name,
		name + "[0]",
		name + "[1]",
		value,
	}, nil
}

func EndForFactory(p *core.Parser, config *core.Configuration) (core.Tag, error) {
	return endFor, nil
}

type For struct {
	*Common
	name      string
	keyName   string
	valueName string
	value     core.Value
}

func (f *For) AddSibling(tag core.Tag) error {
	return errors.New(fmt.Sprintf("%q does not belong inside of a for"))
}

func (f *For) Render(writer io.Writer, data map[string]interface{}) {
	resolved := f.value.Resolve(data)
	if s, ok := resolved.(string); ok {
		f.iterateString(writer, data, s)
	} else {
		value := reflect.ValueOf(resolved)
		kind := value.Kind()
		if kind == reflect.Array || kind == reflect.Slice {
			f.iterateArray(writer, data, value)
		} else if kind == reflect.Map {
			f.iterateMap(writer, data, value)
		}
	}
}

func (f *For) Name() string {
	return "for"
}

func (f *For) Type() core.TagType {
	return core.LoopTag
}

func (f *For) iterateString(writer io.Writer, data map[string]interface{}, s string) {
	length := len(s)
	if length == 0 {
		return
	}

	state := f.loopSetup(length, data, false)
	defer f.loopTeardown(state, data, false)

	for i := 0; i < length; i++ {
		f.loopIteration(state, i, length, data)
		data[f.name] = string(s[i])
		f.Common.Render(writer, data)
	}
}

func (f *For) iterateArray(writer io.Writer, data map[string]interface{}, value reflect.Value) {
	length := value.Len()
	if length == 0 {
		return
	}

	state := f.loopSetup(length, data, false)
	defer f.loopTeardown(state, data, false)

	for i := 0; i < length; i++ {
		f.loopIteration(state, i, length, data)
		data[f.name] = value.Index(i).Interface()
		f.Common.Render(writer, data)
	}
}

func (f *For) iterateMap(writer io.Writer, data map[string]interface{}, value reflect.Value) {
	length := value.Len()
	if length == 0 {
		return
	}

	state := f.loopSetup(length, data, true)
	defer f.loopTeardown(state, data, true)

	keys := value.MapKeys()
	for i := 0; i < length; i++ {
		f.loopIteration(state, i, length, data)
		key := keys[i]
		data[f.keyName] = key.Interface()
		data[f.valueName] = value.MapIndex(key).Interface()
		f.Common.Render(writer, data)
	}
}

func (f *For) loopSetup(length int, data map[string]interface{}, isMap bool) *State {
	state := &State{
		originalForLoop: data["forloop"],
		Length:          length,
	}
	data["forloop"] = state
	if isMap {
		state.key = data[f.keyName]
		state.value = data[f.valueName]
	} else {
		state.value = data[f.name]
	}
	return state
}

func (f *For) loopIteration(state *State, i, length int, data map[string]interface{}) {
	state.Index = i + 1
	state.Index0 = i
	state.RIndex = length - i
	state.RIndex0 = length - i - 1
	state.First = i == 0
	state.Last = i == length-1
}

func (f *For) loopTeardown(state *State, data map[string]interface{}, isMap bool) {
	data["forloop"] = state.originalForLoop
	if isMap {
		data[f.keyName] = state.key
		data[f.valueName] = state.value
	} else {
		data[f.name] = state.value
	}
}

type State struct {
	key             interface{}
	value           interface{}
	originalForLoop interface{}
	Length          int
	Index           int
	Index0          int
	RIndex          int
	RIndex0         int
	First           bool
	Last            bool
}<commit_msg>supporting offset and limit on for tag<commit_after>package tags

import (
	"errors"
	"fmt"
	"github.com/karlseguin/liquid/core"
	"io"
	"reflect"
)

var (
	endFor = &End{"for"}
)

func ForFactory(p *core.Parser, config *core.Configuration) (core.Tag, error) {
	start := p.Position
	name := p.ReadName()
	if len(name) == 0 {
		return nil, p.Error("Invalid variable name in for tag", start)
	}
	if p.SkipSpaces() != 'i' || p.Left() < 3 || p.Data[p.Position+1] != 'n' || !core.IsTokenEnd(p.Data[p.Position+2]) {
		return nil, p.Error("Expecting keyword 'in' after variable name in for tag", start)
	}
	p.ForwardBy(2)

	value, err := p.ReadValue()
	if err != nil {
		return nil, err
	}

	f := &For{
		Common:    NewCommon(),
		name:      name,
		keyName:   name + "[0]",
		valueName: name + "[1]",
		value:     value,
	}

	for {
		name := p.ReadName()
		if name == "" {
			break
		}
		if name == "limit" {
			if p.SkipUntil(':') != ':' {
				return nil, p.Error("Expecting ':' after limit in for tag", start)
			}
			p.Forward()
			limit, err := p.ReadValue()
			if err != nil {
				return nil, err
			}
			f.limit = limit
		} else if name == "offset" {
			if p.SkipUntil(':') != ':' {
				return nil, p.Error("Expecting ':' after offset in for tag", start)
			}
			p.Forward()
			offset, err := p.ReadValue()
			if err != nil {
				return nil, err
			}
			f.offset = offset
		} else if name == "reverse" {
			f.reverse = true
		} else {
			return nil, p.Error(fmt.Sprint("%q is an inknown modifier in for tag", name), start)
		}
	}
	p.SkipPastTag()
	return f, nil
}

func EndForFactory(p *core.Parser, config *core.Configuration) (core.Tag, error) {
	return endFor, nil
}

type For struct {
	*Common
	name      string
	keyName   string
	valueName string
	reverse   bool
	limit     core.Value
	offset    core.Value
	value     core.Value
}

func (f *For) AddSibling(tag core.Tag) error {
	return errors.New(fmt.Sprintf("%q does not belong inside of a for"))
}

func (f *For) Render(writer io.Writer, data map[string]interface{}) {
	value := reflect.ValueOf(f.value.Resolve(data))
	kind := value.Kind()

	if kind == reflect.Array || kind == reflect.Slice || kind == reflect.String || kind == reflect.Map {
		length := value.Len()
		if length == 0 {
			return
		}

		state := &LoopState{
			data:            data,
			writer:          writer,
			originalForLoop: data["forloop"],
			Length:          length,
		}
		data["forloop"] = state
		if f.limit != nil {
			if limit, ok := core.ToInt(f.limit.Resolve(data)); ok && limit < length {
				state.Length = limit
			}
		}

		if f.offset != nil {
			if offset, ok := core.ToInt(f.offset.Resolve(data)); ok {
				state.offset = offset
				if n := state.Length + state.offset; n < state.Length {
					state.Length = n
				} else if length < state.offset+state.Length {
					state.Length = length - offset
				}
			}
		}
		println(state.offset, state.Length)

		defer f.loopTeardown(state)
		if kind == reflect.Map {
			state.isMap = true
			state.key = data[f.keyName]
			state.value = data[f.valueName]
			f.iterateMap(state, value)
		} else {
			state.value = data[f.name]
			f.iterateArray(state, value, kind == reflect.String)
		}
	}
}

func (f *For) Name() string {
	return "for"
}

func (f *For) Type() core.TagType {
	return core.LoopTag
}

func (f *For) iterateArray(state *LoopState, value reflect.Value, isString bool) {
	length, data, writer := state.Length, state.data, state.writer
	for i := 0; i < length; i++ {
		f.loopIteration(state, i)
		offsetI := i + state.offset
		item := value.Index(offsetI).Interface()
		if isString {
			item = string(item.(uint8))
		}
		data[f.name] = item
		f.Common.Render(writer, data)
	}
}

func (f *For) iterateMap(state *LoopState, value reflect.Value) {
	keys := value.MapKeys()
	length, data, writer := state.Length, state.data, state.writer
	for i := 0; i < length; i++ {
		f.loopIteration(state, i)
		offsetI := i + state.offset
		key := keys[offsetI]
		data[f.keyName] = key.Interface()
		data[f.valueName] = value.MapIndex(key).Interface()
		f.Common.Render(writer, data)
	}
}

func (f *For) loopIteration(state *LoopState, i int) {
	l1 := state.Length - 1
	state.Index = i + 1
	state.Index0 = i
	state.RIndex = state.Length - i
	state.RIndex0 = l1 - i
	state.First = i == 0
	state.Last = i == l1
}

func (f *For) loopTeardown(state *LoopState) {
	data := state.data
	data["forloop"] = state.originalForLoop
	if state.isMap {
		data[f.keyName] = state.key
		data[f.valueName] = state.value
	} else {
		data[f.name] = state.value
	}
}

type LoopState struct {
	data            map[string]interface{}
	writer          io.Writer
	key             interface{}
	value           interface{}
	originalForLoop interface{}
	offset          int
	isMap           bool
	Length          int
	Index           int
	Index0          int
	RIndex          int
	RIndex0         int
	First           bool
	Last            bool
}
{'fraction_non_alphanumeric': 0.1520507591207795, 'fraction_numerical': 0.004758667573079538, 'mean_word_length': 2.9941176470588236, 'pattern_counts': {'":': 0, '<': 13, '<?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': '2789456', 'n_tokens_mistral': 3439, 'n_tokens_neox': 3205, 'n_words': 846}