func (env *Env) Eval(nodes []Sexp) []string {
	res := make([]string, 0)
	frame := StackFrame{}
	for _, node := range nodes {
		curr := node.Eval(env, &frame, false)
		if curr != nil {
			res = append(res, curr.String())
		}
	}
	return res
}
evaluates and interprets our AST
package lispy
import (
	"errors"
	"fmt"
	"log"
	"strings"
)
type Env struct {
	parent *Env
	store  map[string]Value
	steps int
}
type Value interface {
	String() string
}
type FunctionValue struct {
	defn *SexpFunctionLiteral
}
type StackFrame struct {
	args []Sexp
}
func (env Env) String() string {
	data := make([]string, 0)
	for key, val := range env.store {
		data = append(data, key+":"+val.String())
	}
	return strings.Join(data, " ")
}
func (f FunctionValue) String() string {
	return fmt.Sprintf("function value: %s", f.defn.String())
}
const maxSteps = 200000
func returnDefinedFunctions() map[string]LispyUserFunction {
	functions := make(map[string]LispyUserFunction)
	functions["car"] = car
	functions["cdr"] = cdr
	functions["cons"] = cons
	functions["+"] = add
	functions["-"] = minus
	functions["/"] = divide
	functions["*"] = multiply
	functions["#"] = expo
	functions["%"] = modulo
	functions["="] = equal
	functions[">="] = gequal
	functions["<="] = lequal
	functions[">"] = gthan
	functions["<"] = lthan
	functions["and"] = and
	functions["or"] = or
	functions["not"] = not
	functions["println"] = printlnStatement
	functions["list"] = createList
	functions["type"] = typeOf
	functions["quote"] = quote
	functions["rand"] = random
	functions["number"] = number
	functions["symbol"] = symbol
	functions["readline"] = readline
	functions["str"] = str
	functions["quote?"] = isQuote
	functions["applyTo"] = applyTo
	functions["readstring"] = readstring
	return functions
}
func InitState() *Env {
	env := new(Env)
	env.store = make(map[string]Value)
	for key, function := range returnDefinedFunctions() {
		env.store[key] = makeUserFunction(key, function)
	}
	env.steps = maxSteps
	errLib := EvalSourceIO(lib, env)
	if errLib != nil {
		log.Fatal("Error loading library packages of lispy")
	}
	return env
}
func (s SexpSymbol) Eval(env *Env, frame *StackFrame, allowThunk bool) Sexp {
	dec(env)
	switch s.ofType {
	case TRUE, FALSE:
		return s
	case STRING:
		if s.value == "" {
			return SexpSymbol{ofType: FALSE, value: "false"}
		}
		return s
	case IF:
		frame.args = append(frame.args, getSexpSymbolFromBool(allowThunk))
		return conditionalStatement(env, s.value, frame.args)
	case DEFINE:
		return varDefinition(env, frame.args[0].String(), frame.args[1:])
	case QUOTE:
		return s
	case SYMBOL:
		if len(frame.args) == 0 {
			return getVarBinding(env, s.value, frame.args)
		} else if s.value == "swap" {
			return swap(env, s.value, frame.args)
		}
		argList, isList := frame.args[0].(SexpPair)
		if !isList {
			log.Fatal("Error trying to parse arguments for function call")
		}
		if s.value == "fn" {
			params, isArray := argList.head.(SexpArray)
			if !isArray {
				log.Fatal("Error parsing anonymous function in macro expansion!")
			}
			bodyFunc, isValid := argList.tail.(SexpPair)
			if !isValid {
				log.Fatal("Error macroexpanding anon function!")
			}
			anonFunc := SexpFunctionLiteral{name: "fn", arguments: params, body: bodyFunc.head, userfunc: nil, macro: false}
			return anonFunc
		}
		funcCall := SexpFunctionCall{name: s.value, arguments: argList}
		return funcCall.Eval(env, frame, allowThunk)
	default:
		fmt.Println(s.ofType, " ", s.value, " args: ", frame.args)
		log.Fatal("Uh oh, weird symbol my dude")
		return nil
	}
}
func (s SexpFunctionLiteral) Eval(env *Env, frame *StackFrame, allowThunk bool) Sexp {
	funcDefinition := FunctionValue{defn: &s}
	frame.args = append(frame.args, SexpSymbol{ofType: STRING, value: s.name})
	funcDefinition.Eval(env, frame, allowThunk)
	dec(env)
	return funcDefinition
}
func (s SexpFunctionCall) Eval(env *Env, frame *StackFrame, allowThunk bool) Sexp {
	functionCallEnv := new(Env)
	functionCallEnv.store = make(map[string]Value)
	for key, element := range env.store {
		functionCallEnv.store[key] = element
	}
	functionCallEnv.steps = env.steps
	functionCallEnv.parent = env
	dec(env)
	return evalFunc(functionCallEnv, &s, allowThunk)
}
func (n SexpPair) Eval(env *Env, frame *StackFrame, allowThunk bool) Sexp {
	var toReturn Sexp
	if n.head == nil {
		return SexpPair{}
	}
	tail, isTail := n.tail.(SexpPair)
	switch head := n.head.(type) {
	case SexpSymbol:
		symbol, ok := n.head.(SexpSymbol)
		if !ok {
			log.Fatal("error trying to interpret symbol")
		}
		arguments := make([]Sexp, 0)
		switch symbol.ofType {
		case DEFINE:
			if !isTail {
				log.Fatal("Unexpected definition, missing value!")
			}
			newFrame := StackFrame{args: makeList(tail)}
			toReturn = symbol.Eval(env, &newFrame, allowThunk)
		case QUOTE:
			if !isTail {
				log.Fatal("Error trying to interpret quote")
			}
			toReturn = tail.head
		case IF:
			if !isTail {
				fmt.Println("Error interpreting condition for the if statement")
			}
			arguments = append(arguments, tail.head.Eval(env, frame, false))
			statements, isValid := tail.tail.(SexpPair)
			if !isValid {
				log.Fatal("Error please provide valid responses to the if condition!")
			}
			res := makeList(statements)
			arguments = append(arguments, res...)
			newFrame := StackFrame{args: arguments}
			toReturn = symbol.Eval(env, &newFrame, allowThunk)
		case DO:
			if !isTail {
				log.Fatal("Error trying to interpret do statements")
			}
			for {
				if tail.tail != nil {
					toReturn = tail.head.Eval(env, frame, false)
				} else {
					toReturn = tail.head.Eval(env, frame, allowThunk)
				}
				switch tail.tail.(type) {
				case SexpPair:
					tail = tail.tail.(SexpPair)
					continue
				}
				break
			}
		default:
			toReturn = symbol.Eval(env, &StackFrame{args: []Sexp{tail}}, allowThunk)
		}
	case SexpFunctionLiteral:
		if head.name == "fn" {
			head.Eval(env, frame, allowThunk)
			if !isTail && n.tail != nil {
				log.Fatal("Error interpreting anonymous function parameters")
			}
			funcCall := SexpFunctionCall{name: "fn", arguments: tail, body: nil}
			toReturn = funcCall.Eval(env, frame, allowThunk)
		} else {
			toReturn = head.Eval(env, frame, allowThunk)
			if n.tail != nil {
				log.Fatal("Error interpreting function declaration or literal - ensure only one Sexp in body of function literal!")
			}
		}
	case SexpFunctionCall:
		toReturn = head.Eval(env, frame, allowThunk)
	case SexpPair:
		original, ok := n.head.(SexpPair)
		if ok {
			toReturn = original.Eval(env, frame, allowThunk)
			funcLiteral, isFuncLiteral := toReturn.(SexpFunctionLiteral)
			if isFuncLiteral && funcLiteral.name == "fn" {
				toReturn = (SexpPair{head: funcLiteral, tail: n.tail}).Eval(env, frame, allowThunk)
			} else {
				toReturn = n
			}
		} else {
			toReturn = SexpSymbol{FALSE, "false"}
		}
	default:
		toReturn = n
	}
	dec(env)
	return toReturn
}
func (arr SexpArray) Eval(env *Env, frame *StackFrame, allowThunk bool) Sexp {
	new := make([]Sexp, 0)
	for index := range arr.value {
		new = append(new, arr.value[index].Eval(env, frame, allowThunk))
	}
	dec(env)
	return SexpArray{ofType: ARRAY, value: new}
}
func (s SexpFloat) Eval(env *Env, frame *StackFrame, allowThunk bool) Sexp {
	dec(env)
	return s
}
func (s SexpInt) Eval(env *Env, frame *StackFrame, allowThunk bool) Sexp {
	dec(env)
	return s
}
func dec(env *Env) {
	if env.steps != maxSteps {
		env.steps -= 1
		if env.steps < 0 {
			log.Fatal("Reached maximum recursion depth :(")
		}
	}
}