<commit_before>package postal

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

type Parser interface {
	// Parseは、cから値を受信したあと必要な加工を行い、c1へ送信する。
	Parse(c <-chan interface{}, c1 chan<- interface{})
}

type entryHandlerFunc func(entry *Entry) *Entry

func (f entryHandlerFunc) Parse(c <-chan interface{}, c1 chan<- interface{}) {
	defer close(c1)
	for v := range c {
		if err, ok := v.(error); ok {
			c1 <- err
			break
		}
		c1 <- f(v.(*Entry))
	}
}

var (
	// 行連結が未解決のままとなった場合のエラー
	IncompleteEntry = errors.New("incomplete entry")
)

// entryが完結している場合はtrue、次のエントリと連結する場合はfalse
type entryCollectorFunc func(entry *Entry) bool

func (f entryCollectorFunc) Parse(c <-chan interface{}, c1 chan<- interface{}) {
	defer close(c1)
	for v := range c {
		if err, ok := v.(error); ok {
			c1 <- err
			return
		}
		entry := v.(*Entry)
		for !f(entry) {
			v, ok := <-c
			if !ok {
				c1 <- IncompleteEntry
				return
			}
			if err, ok := v.(error); ok {
				c1 <- err
				return
			}
			entry1 := v.(*Entry)
			entry.Town = entry.Town.Combine(entry1.Town)
		}
		c1 <- entry
	}
}

type entryExpanderFunc func(entry *Entry) []*Entry

func (f entryExpanderFunc) Parse(c <-chan interface{}, c1 chan<- interface{}) {
	defer close(c1)
	for v := range c {
		if err, ok := v.(error); ok {
			c1 <- err
			return
		}
		entry := v.(*Entry)
		a1, err := tokenDelim{'(', ')', '、'}.Expand(entry.Town.Text)
		if err != nil {
			c1 <- err
			return
		}
		a2, err := tokenDelim{'(', ')', '､'}.Expand(entry.Town.Ruby)
		if err != nil {
			c1 <- err
			return
		}
		if len(a1) != len(a2) {
			// TODO
		}
		for i, _ := range a1 {
			entry1 := new(Entry)
			*entry1 = *entry
			entry1.Town = Name{a1[i], a2[i]}
			c1 <- entry1
		}
	}
}

type tokenDelim struct {
	TokenBegin rune
	TokenEnd   rune
	Delim      rune
}

// （）の中の文字列を展開して配列で返す。
// "ほげ、ふが" => ["ほげ", "ふが"]
// "1〜3" => ["1", "2", "3"]
func (delim tokenDelim) Expand(s string) ([]string, error) {
	t := []rune(s)
	for i := 0; i < len(t); i++ {
		switch t[i] {
		case delim.TokenBegin:
			expr, err := delim.Expr(t[i+1:])
			if err != nil {
				return nil, err
			}
			tokens := strings.FieldsFunc(string(expr), func(c rune) bool {
				return c == delim.Delim
			})
			a := make([]string, len(tokens))
			for j, token := range tokens {
				// "その他"だけは特別扱い
				if token == "その他" || token == "ｿﾉﾀ" {
					token = ""
				}
				a[j] = string(t[0:i]) + token + string(t[i+len(expr)+2:])
			}
			return a, nil
		case delim.TokenEnd:
			return nil, fmt.Errorf("missing %q", delim.TokenBegin)
		}
	}
	return []string{s}, nil
}

func (delim tokenDelim) Expr(s []rune) ([]rune, error) {
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case delim.TokenBegin:
			return nil, fmt.Errorf("missing %q", delim.TokenEnd)
		case delim.TokenEnd:
			return s[0:i], nil
		}
	}
	return nil, fmt.Errorf("missing %q", delim.TokenEnd)
}

var parserFilters = []Parser{
	entryHandlerFunc(func(entry *Entry) *Entry {
		if entry.Town.Text == "以下に掲載がない場合" {
			entry.Town.Text = ""
		}
		if entry.Town.Ruby == "ｲｶﾆｹｲｻｲｶﾞﾅｲﾊﾞｱｲ" {
			entry.Town.Ruby = ""
		}<commit_msg>1〜3形式の省略記法に対応<commit_after>package postal

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
)

type Parser interface {
	// Parseは、cから値を受信したあと必要な加工を行い、c1へ送信する。
	Parse(c <-chan interface{}, c1 chan<- interface{})
}

type entryHandlerFunc func(entry *Entry) *Entry

func (f entryHandlerFunc) Parse(c <-chan interface{}, c1 chan<- interface{}) {
	defer close(c1)
	for v := range c {
		if err, ok := v.(error); ok {
			c1 <- err
			break
		}
		c1 <- f(v.(*Entry))
	}
}

var (
	// 行連結が未解決のままとなった場合のエラー
	IncompleteEntry = errors.New("incomplete entry")
)

// entryが完結している場合はtrue、次のエントリと連結する場合はfalse
type entryCollectorFunc func(entry *Entry) bool

func (f entryCollectorFunc) Parse(c <-chan interface{}, c1 chan<- interface{}) {
	defer close(c1)
	for v := range c {
		if err, ok := v.(error); ok {
			c1 <- err
			return
		}
		entry := v.(*Entry)
		for !f(entry) {
			v, ok := <-c
			if !ok {
				c1 <- IncompleteEntry
				return
			}
			if err, ok := v.(error); ok {
				c1 <- err
				return
			}
			entry1 := v.(*Entry)
			entry.Town = entry.Town.Combine(entry1.Town)
		}
		c1 <- entry
	}
}

type entryExpanderFunc func(entry *Entry) []*Entry

func (f entryExpanderFunc) Parse(c <-chan interface{}, c1 chan<- interface{}) {
	textRule := Rule{
		TokenBegin: '(',
		TokenEnd:   ')',
		Delim:      '、',
		Range:      '〜',
	}
	rubyRule := Rule{
		TokenBegin: '(',
		TokenEnd:   ')',
		Delim:      '､',
		Range:      '-',
	}
	defer close(c1)
	for v := range c {
		if err, ok := v.(error); ok {
			c1 <- err
			return
		}
		entry := v.(*Entry)
		a1, err := textRule.Eval(entry.Town.Text)
		if err != nil {
			c1 <- err
			return
		}
		a2, err := rubyRule.Eval(entry.Town.Ruby)
		if err != nil {
			c1 <- err
			return
		}
		if len(a1) != len(a2) {
			// TODO
		}
		for i, _ := range a1 {
			entry1 := new(Entry)
			*entry1 = *entry
			entry1.Town = Name{a1[i], a2[i]}
			c1 <- entry1
		}
	}
}

// RuleはKEN_ALL.CSVで複数の要素をまとめた書式をあらわす。
type Rule struct {
	// 範囲書式の開始文字。たとえば"（"など。
	TokenBegin rune
	// 複数書式の終了文字。たとえば"）"など。
	TokenEnd rune
	// 複数書式で要素を分割する文字。たとえば"、"など。
	Delim rune
	// 複数書式で範囲をあらわす文字。たとえば"〜"など。
	Range rune
}

// Evalは（）の中の文字列を展開して、一連の文字列を配列で返す。
// "あああ（ほげ、ふが）" => ["あああほげ", "あああふが"]
// "1〜3" => ["1", "2", "3"]
func (rule Rule) Eval(s string) ([]string, error) {
	t := []rune(s)
	for i := 0; i < len(t); i++ {
		switch t[i] {
		case rule.TokenBegin:
			expr, err := rule.Expr(t[i+1:])
			if err != nil {
				return nil, err
			}
			tokens, err := rule.Tokens(expr)
			if err != nil {
				return nil, err
			}
			a := make([]string, len(tokens))
			for j, token := range tokens {
				// "その他"だけは特別扱い
				if token == "その他" || token == "ｿﾉﾀ" {
					token = ""
				}
				a[j] = string(t[0:i]) + token + string(t[i+len(expr)+2:])
			}
			return a, nil
		case rule.TokenEnd:
			return nil, fmt.Errorf("missing %q", rule.TokenBegin)
		}
	}
	return []string{s}, nil
}

// Expr returns string inside rule.TokenBegin and rule.TokenEnd.
func (rule Rule) Expr(s []rune) ([]rune, error) {
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case rule.TokenBegin:
			return nil, fmt.Errorf("missing %q", rule.TokenEnd)
		case rule.TokenEnd:
			return s[0:i], nil
		}
	}
	return nil, fmt.Errorf("missing %q", rule.TokenEnd)
}

// Tokensはrule.Delimとrule.Rangeを評価して展開後の文字列配列を返す。
func (rule Rule) Tokens(expr []rune) (tokens []string, err error) {
	stage1 := strings.FieldsFunc(string(expr), func(c rune) bool {
		return c == rule.Delim
	})

	for _, s := range stage1 {
		stage2, err := rule.Expand(s)
		if err != nil {
			return nil, err
		}
		for _, token := range stage2 {
			tokens = append(tokens, token)
		}
	}
	return
}

// Expandはrule.Rangeを展開して複数の文字列を返す。
func (rule Rule) Expand(token string) (tokens []string, err error) {
	re := regexp.MustCompile(`(\d+)` + string(rule.Range) + `(\d+)`)
	m := re.FindStringSubmatchIndex(token)
	if m == nil {
		tokens = append(tokens, token)
		return
	}
	prefix := token[0:m[2]]
	bp, err := strconv.Atoi(token[m[2]:m[3]])
	if err != nil {
		return
	}
	ep, err := strconv.Atoi(token[m[4]:m[5]])
	if err != nil {
		return
	}
	suffix := token[m[5]:]
	for i := bp; i <= ep; i++ {
		tokens = append(tokens, fmt.Sprintf("%s%d%s", prefix, i, suffix))
	}
	return
}

var parserFilters = []Parser{
	entryHandlerFunc(func(entry *Entry) *Entry {
		if entry.Town.Text == "以下に掲載がない場合" {
			entry.Town.Text = ""
		}
		if entry.Town.Ruby == "ｲｶﾆｹｲｻｲｶﾞﾅｲﾊﾞｱｲ" {
			entry.Town.Ruby = ""
		}
{'fraction_non_alphanumeric': 0.1829300717894177, 'fraction_numerical': 0.01289550651422494, 'mean_word_length': 2.9741151611199155, 'pattern_counts': {'":': 0, '<': 46, '<?xml version=': 0, '>': 7, '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': '4248280', 'n_tokens_mistral': 3789, 'n_tokens_neox': 3374, 'n_words': 736}