<commit_before>/*
 * gomacro - A Go intepreter with Lisp-like macros
 *
 * Copyright (C) 2017 <NAME>
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * type.go
 *
 *  Created on: Feb 13, 2017
 *      Author: <NAME>
 */

package interpreter

import (
	"fmt"
	"go/ast"
	r "reflect"
)

func TypeOf(value r.Value) r.Type {
	if value == None || value == Nil {
		return typeOfInterface
	}
	return value.Type()
}

func (env *Env) evalExpr1OrType(node ast.Expr) (val r.Value, t r.Type) {
	defer func() {
		if r := recover(); r != nil {
			switch r.(type) {
			case RuntimeError:
				t = env.evalType(node)
			default:
				panic(r)
			}
		}
	}()
	val = env.evalExpr1(node)
	return val, nil
}

func (env *Env) evalType(node ast.Expr) r.Type {
	stars := 0
	for {
		switch expr := node.(type) {
		case *ast.StarExpr:
			stars++
			node = expr.X
			continue
		case *ast.ParenExpr:
			node = expr.X
			continue
		}
		break
	}

	var t r.Type
	switch node := node.(type) {
	case *ast.ArrayType: // also for slices
		t = env.evalTypeArray(node)
	case *ast.ChanType:
		t = env.evalType(node.Value)
		dir := r.BothDir
		if node.Dir == ast.SEND {
			dir = r.SendDir
		} else if node.Dir == ast.RECV {
			dir = r.RecvDir
		}
		t = r.ChanOf(dir, t)
	case *ast.FuncType:
		t, _, _ = env.evalTypeFunction(node)
	case *ast.Ident:
		t = env.evalTypeIdentifier(node.Name)
	case *ast.InterfaceType:
		t, _ = env.evalTypeInterface(node)
	case *ast.MapType:
		kt := env.evalType(node.Key)
		vt := env.evalType(node.Value)
		t = r.MapOf(kt, vt)
	case *ast.SelectorExpr:
		if pkgIdent, ok := node.X.(*ast.Ident); ok {
			pkgv := env.evalIdentifier(pkgIdent)
			if pkg, ok := pkgv.Interface().(*Env); ok {
				name := node.Sel.Name
				if t, ok = pkg.Types[name]; !ok {
					env.Errorf("not a type: %v <%v>", node, r.TypeOf(node))
				}
			} else {
				env.Errorf("not a package: %v = %v <%v>", pkgIdent, pkgv, TypeOf(pkgv))
			}
		} else {
			env.Errorf("unimplemented qualified type, expecting packageName.identifier: %v <%v>", node, r.TypeOf(node))
		}
	case *ast.StructType:
		// env.Debugf("evalType() struct declaration: %v <%v>", node, r.TypeOf(node))
		types, names := env.evalTypeFields(node.Fields)
		// env.Debugf("evalType() struct names and types: %v %v", types, names)
		fields := makeStructFields(env.FileEnv().Path, names, types)
		// env.Debugf("evalType() struct fields: %#v", fields)
		t = r.StructOf(fields)
	case nil:
		// type can be omitted in many case - then we must perform type inference
		break
	default:
		// TODO which types are still missing?
		env.Errorf("unimplemented type: %v <%v>", node, r.TypeOf(node))
	}
	for i := 0; i < stars; i++ {
		t = r.PtrTo(t)
	}
	return t
}

func (env *Env) evalTypeArray(node *ast.ArrayType) r.Type {
	t := env.evalType(node.Elt)
	n := node.Len
	switch n := n.(type) {
	case *ast.Ellipsis:
		env.Errorf("evalType(): unimplemented array type with ellipsis: %v", node, r.TypeOf(node))
	case nil:
		t = r.SliceOf(t)
	default:
		count := env.evalExpr1(n).Int()
		t = r.ArrayOf(int(count), t)
	}
	return t
}

func (env *Env) evalTypeFunction(node *ast.FuncType) (t r.Type, argNames []string, resultNames []string) {
	argTypes, argNames := env.evalTypeFields(node.Params)
	resultTypes, resultNames := env.evalTypeFields(node.Results)
	return r.FuncOf(argTypes, resultTypes, false /* TODO variadic*/), argNames, resultNames
}

func (env *Env) evalTypeFields(fields *ast.FieldList) ([]r.Type, []string) {
	types := make([]r.Type, 0)
	names := zeroStrings
	if fields == nil || len(fields.List) == 0 {
		return types, names
	}
	for _, f := range fields.List {

		t := env.evalType(f.Type)
		if len(f.Names) == 0 {
			types = append(types, t)
			names = append(names, "_")
			// env.Debugf("evalTypeFields() %v -> %v", f.Type, t)
		} else {
			for _, ident := range f.Names {
				types = append(types, t)
				names = append(names, ident.Name)
				// Debugf("evalTypeFields() %v %v -> %v", ident.Name, f.Type, t)
			}
		}
	}
	return types, names
}

func (env *Env) evalTypeIdentifier(name string) r.Type {
	for env != nil {
		if t, ok := env.Types[name]; ok {
			return t
		}
		env = env.Outer
	}
	env.Errorf("not a type: %v", name)
	return nil
}

func (env *Env) evalTypeInterface(node *ast.InterfaceType) (t r.Type, methodNames []string) {
	if node.Methods != nil && len(node.Methods.List) != 0 {
		env.Errorf("unimplemented type: %v <%v>", node, r.TypeOf(node))
		return nil, nil
	}
	return typeOfInterface, zeroStrings
}

func makeStructFields(pkgPath string, names []string, types []r.Type) []r.StructField {
	// pkgIdentifier := sanitizeIdentifier(pkgPath)
	fields := make([]r.StructField, len(names))
	var offset, next uintptr
	for i, name := range names {
		t := types[i]
		offset, next = alignStructField(next, t)<commit_msg>evalTypeIdentifier(): fixed nil pointer dereference in case of undefined identifiers<commit_after>/*
 * gomacro - A Go intepreter with Lisp-like macros
 *
 * Copyright (C) 2017 <NAME>
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * type.go
 *
 *  Created on: Feb 13, 2017
 *      Author: <NAME>
 */

package interpreter

import (
	"fmt"
	"go/ast"
	r "reflect"
)

func TypeOf(value r.Value) r.Type {
	if value == None || value == Nil {
		return typeOfInterface
	}
	return value.Type()
}

func (env *Env) evalExpr1OrType(node ast.Expr) (val r.Value, t r.Type) {
	defer func() {
		if r := recover(); r != nil {
			switch r.(type) {
			case RuntimeError:
				t = env.evalType(node)
			default:
				panic(r)
			}
		}
	}()
	val = env.evalExpr1(node)
	return val, nil
}

func (env *Env) evalType(node ast.Expr) r.Type {
	stars := 0
	for {
		switch expr := node.(type) {
		case *ast.StarExpr:
			stars++
			node = expr.X
			continue
		case *ast.ParenExpr:
			node = expr.X
			continue
		}
		break
	}

	var t r.Type
	switch node := node.(type) {
	case *ast.ArrayType: // also for slices
		t = env.evalTypeArray(node)
	case *ast.ChanType:
		t = env.evalType(node.Value)
		dir := r.BothDir
		if node.Dir == ast.SEND {
			dir = r.SendDir
		} else if node.Dir == ast.RECV {
			dir = r.RecvDir
		}
		t = r.ChanOf(dir, t)
	case *ast.FuncType:
		t, _, _ = env.evalTypeFunction(node)
	case *ast.Ident:
		t = env.evalTypeIdentifier(node.Name)
	case *ast.InterfaceType:
		t, _ = env.evalTypeInterface(node)
	case *ast.MapType:
		kt := env.evalType(node.Key)
		vt := env.evalType(node.Value)
		t = r.MapOf(kt, vt)
	case *ast.SelectorExpr:
		if pkgIdent, ok := node.X.(*ast.Ident); ok {
			pkgv := env.evalIdentifier(pkgIdent)
			if pkg, ok := pkgv.Interface().(*Env); ok {
				name := node.Sel.Name
				if t, ok = pkg.Types[name]; !ok {
					env.Errorf("not a type: %v <%v>", node, r.TypeOf(node))
				}
			} else {
				env.Errorf("not a package: %v = %v <%v>", pkgIdent, pkgv, TypeOf(pkgv))
			}
		} else {
			env.Errorf("unimplemented qualified type, expecting packageName.identifier: %v <%v>", node, r.TypeOf(node))
		}
	case *ast.StructType:
		// env.Debugf("evalType() struct declaration: %v <%v>", node, r.TypeOf(node))
		types, names := env.evalTypeFields(node.Fields)
		// env.Debugf("evalType() struct names and types: %v %v", types, names)
		fields := makeStructFields(env.FileEnv().Path, names, types)
		// env.Debugf("evalType() struct fields: %#v", fields)
		t = r.StructOf(fields)
	case nil:
		// type can be omitted in many case - then we must perform type inference
		break
	default:
		// TODO which types are still missing?
		env.Errorf("unimplemented type: %v <%v>", node, r.TypeOf(node))
	}
	for i := 0; i < stars; i++ {
		t = r.PtrTo(t)
	}
	return t
}

func (env *Env) evalTypeArray(node *ast.ArrayType) r.Type {
	t := env.evalType(node.Elt)
	n := node.Len
	switch n := n.(type) {
	case *ast.Ellipsis:
		env.Errorf("evalType(): unimplemented array type with ellipsis: %v", node, r.TypeOf(node))
	case nil:
		t = r.SliceOf(t)
	default:
		count := env.evalExpr1(n).Int()
		t = r.ArrayOf(int(count), t)
	}
	return t
}

func (env *Env) evalTypeFunction(node *ast.FuncType) (t r.Type, argNames []string, resultNames []string) {
	argTypes, argNames := env.evalTypeFields(node.Params)
	resultTypes, resultNames := env.evalTypeFields(node.Results)
	return r.FuncOf(argTypes, resultTypes, false /* TODO variadic*/), argNames, resultNames
}

func (env *Env) evalTypeFields(fields *ast.FieldList) ([]r.Type, []string) {
	types := make([]r.Type, 0)
	names := zeroStrings
	if fields == nil || len(fields.List) == 0 {
		return types, names
	}
	for _, f := range fields.List {

		t := env.evalType(f.Type)
		if len(f.Names) == 0 {
			types = append(types, t)
			names = append(names, "_")
			// env.Debugf("evalTypeFields() %v -> %v", f.Type, t)
		} else {
			for _, ident := range f.Names {
				types = append(types, t)
				names = append(names, ident.Name)
				// Debugf("evalTypeFields() %v %v -> %v", ident.Name, f.Type, t)
			}
		}
	}
	return types, names
}

func (env *Env) evalTypeIdentifier(name string) r.Type {
	for e := env; e != nil; e = e.Outer {
		if t, ok := e.Types[name]; ok {
			return t
		}
	}
	env.Errorf("undefined identifier: %v", name)
	return nil
}

func (env *Env) evalTypeInterface(node *ast.InterfaceType) (t r.Type, methodNames []string) {
	if node.Methods != nil && len(node.Methods.List) != 0 {
		env.Errorf("unimplemented type: %v <%v>", node, r.TypeOf(node))
		return nil, nil
	}
	return typeOfInterface, zeroStrings
}

func makeStructFields(pkgPath string, names []string, types []r.Type) []r.StructField {
	// pkgIdentifier := sanitizeIdentifier(pkgPath)
	fields := make([]r.StructField, len(names))
	var offset, next uintptr
	for i, name := range names {
		t := types[i]
		offset, next = alignStructField(next, t)
{'fraction_non_alphanumeric': 0.1501195072623644, 'fraction_numerical': 0.0036771465342893914, 'mean_word_length': 3.3136399682791438, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '5961050', 'n_tokens_mistral': 4220, 'n_tokens_neox': 3884, 'n_words': 1139}