<commit_before>package compile

import (
	"fmt"
	"path"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/core"
	"github.com/tisp-lang/tisp/src/lib/ir"
)

type compiler struct {
	env environment
}

func newCompiler() compiler {
	return compiler{env: prelude()}
}

func (c *compiler) compile(module []interface{}) []Output {
	outputs := make([]Output, 0)

	for _, node := range module {
		switch x := node.(type) {
		case ast.LetVar:
			c.env.set(x.Name(), c.exprToThunk(x.Expr()))
		case ast.LetFunction:
			sig := x.Signature()
			ls := x.Lets()

			vars := make([]interface{}, len(ls))
			varIndices := map[string]int{}

			for i, l := range ls {
				cst := l.(ast.LetVar)
				vars[i] = c.exprToIR(sig, varIndices, cst.Expr())
				varIndices[cst.Name()] = sig.Arity() + i
			}

			c.env.set(
				x.Name(),
				ir.CompileFunction(
					c.compileSignature(sig),
					vars,
					c.exprToIR(sig, varIndices, x.Body())))
		case ast.Output:
			outputs = append(outputs, NewOutput(c.exprToThunk(x.Expr()), x.Expanded()))
		case ast.Import:
			for k, v := range SubModule(x.Path() + ".tisp") {
				c.env.set(path.Base(x.Path())+"."+k, v)
			}
		default:
			panic(fmt.Errorf("Invalid type: %#v", x))
		}
	}

	return outputs
}

func (c *compiler) exprToThunk(expr interface{}) *core.Thunk {
	switch x := expr.(type) {
	case string:
		return c.env.get(x)
	case ast.App:
		args := x.Arguments()

		ps := make([]core.PositionalArgument, len(args.Positionals()))
		for i, p := range args.Positionals() {
			ps[i] = core.NewPositionalArgument(c.exprToThunk(p.Value()), p.Expanded())
		}

		ks := make([]core.KeywordArgument, len(args.Keywords()))
		for i, k := range args.Keywords() {
			ks[i] = core.NewKeywordArgument(k.Name(), c.exprToThunk(k.Value()))
		}

		ds := make([]*core.Thunk, len(args.ExpandedDicts()))
		for i, d := range args.ExpandedDicts() {
			ds[i] = c.exprToThunk(d)
		}

		return core.AppWithInfo(
			c.exprToThunk(x.Function()),
			core.NewArguments(ps, ks, ds),
			x.DebugInfo())
	}

	panic("Unreachable")
}

func (c *compiler) compileSignature(sig ast.Signature) core.Signature {
	return core.NewSignature(
		sig.PosReqs(), c.compileOptionalArguments(sig.PosOpts()), sig.PosRest(),
		sig.KeyReqs(), c.compileOptionalArguments(sig.KeyOpts()), sig.KeyRest(),
	)
}

func (c *compiler) compileOptionalArguments(opts []ast.OptionalArgument) []core.OptionalArgument {
	coreOpts := make([]core.OptionalArgument, len(opts))

	for i, opt := range opts {
		coreOpts[i] = core.NewOptionalArgument(opt.Name(), c.exprToThunk(opt.DefaultValue()))
	}

	return coreOpts
}

func (c *compiler) exprToIR(sig ast.Signature, vars map[string]int, expr interface{}) interface{} {
	switch x := expr.(type) {
	case string:<commit_msg>Remove duplicate logics in compiler.exprToThunk()<commit_after>package compile

import (
	"fmt"
	"path"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/core"
	"github.com/tisp-lang/tisp/src/lib/ir"
)

type compiler struct {
	env environment
}

func newCompiler() compiler {
	return compiler{env: prelude()}
}

func (c *compiler) compile(module []interface{}) []Output {
	outputs := make([]Output, 0)

	for _, node := range module {
		switch x := node.(type) {
		case ast.LetVar:
			c.env.set(x.Name(), c.exprToThunk(x.Expr()))
		case ast.LetFunction:
			sig := x.Signature()
			ls := x.Lets()

			vars := make([]interface{}, len(ls))
			varIndices := map[string]int{}

			for i, l := range ls {
				cst := l.(ast.LetVar)
				vars[i] = c.exprToIR(sig, varIndices, cst.Expr())
				varIndices[cst.Name()] = sig.Arity() + i
			}

			c.env.set(
				x.Name(),
				ir.CompileFunction(
					c.compileSignature(sig),
					vars,
					c.exprToIR(sig, varIndices, x.Body())))
		case ast.Output:
			outputs = append(outputs, NewOutput(c.exprToThunk(x.Expr()), x.Expanded()))
		case ast.Import:
			for k, v := range SubModule(x.Path() + ".tisp") {
				c.env.set(path.Base(x.Path())+"."+k, v)
			}
		default:
			panic(fmt.Errorf("Invalid type: %#v", x))
		}
	}

	return outputs
}

func (c *compiler) exprToThunk(expr interface{}) *core.Thunk {
	return core.PApp(ir.CompileFunction(
		core.NewSignature(nil, nil, "", nil, nil, ""),
		nil,
		c.exprToIR(ast.NewSignature(nil, nil, "", nil, nil, ""), nil, expr)))
}

func (c *compiler) compileSignature(sig ast.Signature) core.Signature {
	return core.NewSignature(
		sig.PosReqs(), c.compileOptionalArguments(sig.PosOpts()), sig.PosRest(),
		sig.KeyReqs(), c.compileOptionalArguments(sig.KeyOpts()), sig.KeyRest(),
	)
}

func (c *compiler) compileOptionalArguments(opts []ast.OptionalArgument) []core.OptionalArgument {
	coreOpts := make([]core.OptionalArgument, len(opts))

	for i, opt := range opts {
		coreOpts[i] = core.NewOptionalArgument(opt.Name(), c.exprToThunk(opt.DefaultValue()))
	}

	return coreOpts
}

func (c *compiler) exprToIR(sig ast.Signature, vars map[string]int, expr interface{}) interface{} {
	switch x := expr.(type) {
	case string:
{'fraction_non_alphanumeric': 0.1824773413897281, 'fraction_numerical': 0.0004028197381671702, 'mean_word_length': 4.0110998990918265, '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': '5871808', 'n_tokens_mistral': 1992, 'n_tokens_neox': 1832, 'n_words': 365}