<commit_before>package eval

// Builtin special forms.

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/elves/elvish/parse"
)

type exitusOp func(*evalCtx) exitus
type builtinSpecialCompile func(*compileCtx, *parse.Form) exitusOp

type builtinSpecial struct {
	compile builtinSpecialCompile
}

var builtinSpecials map[string]builtinSpecial

func init() {
	// Needed to avoid initialization loop
	builtinSpecials = map[string]builtinSpecial{
		"var": builtinSpecial{compileVar},
		"set": builtinSpecial{compileSet},
		"del": builtinSpecial{compileDel},

		"use": builtinSpecial{compileUse},

		"fn": builtinSpecial{compileFn},
		"if": builtinSpecial{compileIf},

		"static-typeof": builtinSpecial{compileStaticTypeof},
	}
}

func mayAssign(tvar, tval Type) bool {
	if isAny(tval) || isAny(tvar) {
		return true
	}
	// XXX(xiaq) This is not how you check the equality of two interfaces. But
	// it happens to work when all the Type instances we have are empty
	// structs.
	return tval == tvar
}

func checkSetType(cc *compileCtx, names []string, values []*parse.Compound, vop valuesOp, p parse.Pos) {
	if !vop.tr.mayCountTo(len(names)) {
		cc.errorf(p, "number of variables doesn't match that of values")
	}
	_, more := vop.tr.count()
	if more {
		// TODO Try to check soundness to some extent
		return
	}
	for i, name := range names {
		tval := vop.tr[i].t
		tvar := cc.ResolveVar(splitQualifiedName(name))
		if !mayAssign(tvar, tval) {
			cc.errorf(values[i].Pos, "type mismatch: assigning %#v value to %#v variable", tval, tvar)
		}
	}
}

// ensure that a CompoundNode contains exactly one PrimaryNode.
func ensurePrimary(cc *compileCtx, cn *parse.Compound, msg string) *parse.Primary {
	if len(cn.Nodes) != 1 || cn.Nodes[0].Right != nil {
		cc.errorf(cn.Pos, msg)
	}
	return cn.Nodes[0].Left
}

// ensureVariableOrStringPrimary ensures that a CompoundNode contains exactly
// one PrimaryNode of type VariablePrimary or StringPrimary.
func ensureVariableOrStringPrimary(cc *compileCtx, cn *parse.Compound, msg string) (*parse.Primary, string) {
	pn := ensurePrimary(cc, cn, msg)
	switch pn.Typ {
	case parse.VariablePrimary, parse.StringPrimary:
		return pn, pn.Node.(*parse.String).Text<commit_msg>Better type check for set<commit_after>package eval

// Builtin special forms.

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/elves/elvish/parse"
)

type exitusOp func(*evalCtx) exitus
type builtinSpecialCompile func(*compileCtx, *parse.Form) exitusOp

type builtinSpecial struct {
	compile builtinSpecialCompile
}

var builtinSpecials map[string]builtinSpecial

func init() {
	// Needed to avoid initialization loop
	builtinSpecials = map[string]builtinSpecial{
		"var": builtinSpecial{compileVar},
		"set": builtinSpecial{compileSet},
		"del": builtinSpecial{compileDel},

		"use": builtinSpecial{compileUse},

		"fn": builtinSpecial{compileFn},
		"if": builtinSpecial{compileIf},

		"static-typeof": builtinSpecial{compileStaticTypeof},
	}
}

func mayAssign(tvar, tval Type) bool {
	if isAny(tval) || isAny(tvar) {
		return true
	}
	// XXX(xiaq) This is not how you check the equality of two interfaces. But
	// it happens to work when all the Type instances we have are empty
	// structs.
	return tval == tvar
}

func checkSetType(cc *compileCtx, names []string, values []*parse.Compound, vop valuesOp, p parse.Pos) {
	n, more := vop.tr.count()
	if more {
		if n > len(names) {
			cc.errorf(p, "number of variables (%d) can never match that of values (%d or more)", len(names), n)
		}
		// Only check the variables before the "more" part.
		name = name[:n]
	} else if n != len(names) {
		cc.errorf(p, "number of variables (%d) doesn't match that of values (%d or more)", len(names), n)
	}

	for i, name := range names {
		tval := vop.tr[i].t
		tvar := cc.ResolveVar(splitQualifiedName(name))
		if !mayAssign(tvar, tval) {
			cc.errorf(values[i].Pos, "type mismatch: assigning %#v value to %#v variable", tval, tvar)
		}
	}
}

// ensure that a CompoundNode contains exactly one PrimaryNode.
func ensurePrimary(cc *compileCtx, cn *parse.Compound, msg string) *parse.Primary {
	if len(cn.Nodes) != 1 || cn.Nodes[0].Right != nil {
		cc.errorf(cn.Pos, msg)
	}
	return cn.Nodes[0].Left
}

// ensureVariableOrStringPrimary ensures that a CompoundNode contains exactly
// one PrimaryNode of type VariablePrimary or StringPrimary.
func ensureVariableOrStringPrimary(cc *compileCtx, cn *parse.Compound, msg string) (*parse.Primary, string) {
	pn := ensurePrimary(cc, cn, msg)
	switch pn.Typ {
	case parse.VariablePrimary, parse.StringPrimary:
		return pn, pn.Node.(*parse.String).Text
{'fraction_non_alphanumeric': 0.1264417845484222, 'fraction_numerical': 0.0013057671381936887, 'mean_word_length': 4.517406962785114, 'pattern_counts': {'":': 14, '<': 3, '<?xml version=': 0, '>': 4, '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': '6164942', 'n_tokens_mistral': 1629, 'n_tokens_neox': 1570, 'n_words': 450}