func (i *Interpreter) runSwitch(node *ast.Switch, scope *Scope) DataType {
	var control DataType
	if node.Control == nil {
		control = TRUE
	} else {
		control = i.Interpret(node.Control, scope)
		if control == nil {
			i.reportError(node, "Switch control expression couldn't be interpreted")
			return nil
		}
	}
	thecase, err := i.runSwitchCase(node.Cases, control, scope)
	if err != nil {
		i.reportError(node, err.Error())
		return nil
	}
	if thecase != nil {
		return i.Interpret(thecase.Body, NewScopeFrom(scope))
	}
	if node.Default != nil {
		return i.Interpret(node.Default, NewScopeFrom(scope))
	}
	return nil
}
Interpret a Switch expression.
package interpreter
import (
	"fmt"
	"github.com/fadion/aria/ast"
	"github.com/fadion/aria/lexer"
	"github.com/fadion/aria/library"
	"github.com/fadion/aria/parser"
	"github.com/fadion/aria/reader"
	"github.com/fadion/aria/reporter"
	"io/ioutil"
	"math"
	"path/filepath"
	"strings"
)
type Interpreter struct {
	modules         map[string]*ModuleType
	moduleCache     map[string]map[string]DataType
	importCache     map[string]DataType
	immutables      map[string]*ast.Identifier
	libraryFinished bool
}
func New() *Interpreter {
	return &Interpreter{
		modules:         map[string]*ModuleType{},
		moduleCache:     map[string]map[string]DataType{},
		importCache:     map[string]DataType{},
		immutables:      map[string]*ast.Identifier{},
		libraryFinished: false,
	}
}
func (i *Interpreter) Interpret(node ast.Node, scope *Scope) DataType {
	if err := i.importLibraryModules(scope); err != nil {
		i.reportError(node, err.Error())
		return nil
	}
	switch node := node.(type) {
	case *ast.Program:
		return i.runProgram(node, scope)
	case *ast.Module:
		return i.runModule(node, scope)
	case *ast.ModuleAccess:
		return i.runModuleAccess(node, scope)
	case *ast.Identifier:
		return i.runIdentifier(node, scope)
	case *ast.Let:
		return i.runLet(node, scope)
	case *ast.Var:
		return i.runVar(node, scope)
	case *ast.String:
		return &StringType{Value: node.Value}
	case *ast.Atom:
		return &AtomType{Value: node.Value}
	case *ast.Integer:
		return &IntegerType{Value: node.Value}
	case *ast.Float:
		return &FloatType{Value: node.Value}
	case *ast.Boolean:
		return i.nativeToBoolean(node.Value)
	case *ast.Array:
		return i.runArray(node, scope)
	case *ast.Dictionary:
		return i.runDictionary(node, scope)
	case *ast.Nil:
		return &NilType{}
	case *ast.ExpressionStatement:
		return i.Interpret(node.Expression, scope)
	case *ast.BlockStatement:
		return i.runBlockStatement(node, scope)
	case *ast.PrefixExpression:
		return i.runPrefix(node, scope)
	case *ast.InfixExpression:
		return i.runInfix(node, scope)
	case *ast.Assign:
		return i.runAssign(node, scope)
	case *ast.Pipe:
		return i.runPipe(node, scope)
	case *ast.If:
		return i.runIf(node, scope)
	case *ast.Switch:
		return i.runSwitch(node, scope)
	case *ast.For:
		return i.runFor(node, scope)
	case *ast.Function:
		return &FunctionType{
			Parameters: node.Parameters,
			Body:       node.Body,
			ReturnType: node.ReturnType,
			Variadic:   node.Variadic,
			Scope:      NewScopeFrom(scope),
		}
	case *ast.FunctionCall:
		return i.runFunction(node, scope)
	case *ast.Import:
		return i.runImport(node, scope)
	case *ast.Subscript:
		return i.runSubscript(node, scope)
	case *ast.Return:
		return &ReturnType{Value: i.Interpret(node.Value, scope)}
	case *ast.Break:
		return &BreakType{}
	case *ast.Continue:
		return &ContinueType{}
	case *ast.Placeholder:
		return &PlaceholderType{}
	case *ast.Is:
		return i.runIs(node, scope)
	case *ast.As:
		return i.runAs(node, scope)
	}
	return nil
}
func (i *Interpreter) importLibraryModules(scope *Scope) error {
	if i.libraryFinished {
		return nil
	}
	i.libraryFinished = true
	for _, v := range library.Modules {
		lex := lexer.New(reader.New([]byte(v)))
		if reporter.HasErrors() {
			return fmt.Errorf("Problem reading Standard Library module")
		}
		parse := parser.New(lex)
		program := parse.Parse()
		if reporter.HasErrors() {
			return fmt.Errorf("Problem parsing Standard Library module")
		}
		i.Interpret(program, scope)
	}
	return nil
}
func (i *Interpreter) runProgram(node *ast.Program, scope *Scope) DataType {
	var result DataType
	for _, statement := range node.Statements {
		result = i.Interpret(statement, scope)
	}
	return result
}
func (i *Interpreter) runLet(node *ast.Let, scope *Scope) DataType {
	object := i.Interpret(node.Value, scope)
	if object == nil {
		return nil
	}
	if _, ok := scope.Read(node.Name.Value); ok {
		i.reportError(node, fmt.Sprintf("Identifier '%s' already declared", node.Name.Value))
		return nil
	}
	scope.Write(node.Name.Value, object)
	if _, ok := i.immutables[node.Name.Value]; !ok {
		i.immutables[node.Name.Value] = node.Name
	}
	return object
}
func (i *Interpreter) runVar(node *ast.Var, scope *Scope) DataType {
	object := i.Interpret(node.Value, scope)
	if object == nil {
		return nil
	}
	if _, ok := scope.Read(node.Name.Value); ok {
		i.reportError(node, fmt.Sprintf("Identifier '%s' already declared", node.Name.Value))
		return nil
	}
	scope.Write(node.Name.Value, object)
	return object
}
func (i *Interpreter) runModule(node *ast.Module, scope *Scope) DataType {
	if _, ok := i.modules[node.Name.Value]; ok {
		i.reportError(node, fmt.Sprintf("Module '%s' redeclared", node.Name.Value))
	} else {
		i.modules[node.Name.Value] = &ModuleType{Name: node.Name, Body: node.Body}
	}
	return nil
}
func (i *Interpreter) runModuleAccess(node *ast.ModuleAccess, scope *Scope) DataType {
	scope = NewScope()
	if module, ok := i.modules[node.Object.Value]; ok {
		if results, ok := i.moduleCache[module.Name.Value]; ok {
			if result, ok := results[node.Parameter.Value]; ok {
				return result
			}
		} else {
			results := map[string]DataType{}
			for _, statement := range module.Body.Statements {
				switch sType := statement.(type) {
				case *ast.ExpressionStatement:
					switch eType := sType.Expression.(type) {
					case *ast.Let:  
						result := i.Interpret(statement, scope)
						if result == nil {
							return nil
						}
						results[eType.Name.Value] = result
					default:
						i.reportError(node, "Only LET statements are accepted as Module members")
						return nil
					}
				default:
					i.reportError(node, "Only LET statements are accepted as Module members")
					return nil
				}
			}
			i.moduleCache[module.Name.Value] = results
			if val, ok := i.moduleCache[module.Name.Value][node.Parameter.Value]; ok {
				return val
			} else {
				i.reportError(node, fmt.Sprintf("Member '%s' in module '%s' not found", node.Parameter.Value, node.Object.Value))
				return nil
			}
		}
	}
	i.reportError(node, fmt.Sprintf("%s.%s not found", node.Object.Value, node.Parameter.Value))
	return nil
}
func (i *Interpreter) runIdentifier(node *ast.Identifier, scope *Scope) DataType {
	if object, ok := scope.Read(node.Value); ok {
		return object
	}
	i.reportError(node, fmt.Sprintf("Identifier '%s' not found in current scope", node.Value))
	return nil
}
func (i *Interpreter) runBlockStatement(node *ast.BlockStatement, scope *Scope) DataType {
	var result DataType
	for _, statement := range node.Statements {
		result = i.Interpret(statement, scope)
		if result == nil {
			return nil
		}
		if i.shouldBreakImmediately(result) {
			return result
		}
	}
	return result
}
func (i *Interpreter) runAssign(node *ast.Assign, scope *Scope) DataType {
	var name string
	var original DataType
	var ok bool
	var err error
	switch nodeType := node.Name.(type) {
	case *ast.Identifier:
		name = nodeType.Value
	case *ast.Subscript:
		name = nodeType.Left.(*ast.Identifier).Value
	}
	if original, ok = scope.Read(name); !ok {
		i.reportError(node, fmt.Sprintf("Identifier '%s' not found in current scope", name))
		return nil
	}
	if _, ok = i.immutables[name]; ok {
		i.reportError(node, fmt.Sprintf("Identifier '%s' is immutable", name))
		return nil
	}
	object := i.Interpret(node.Right, scope)
	if object == nil {
		return nil
	}
	switch nodeType := node.Name.(type) {
	case *ast.Subscript:
		object, err = i.runAssignSubscript(nodeType, original, object, scope)
		if err != nil {
			i.reportError(node, err.Error())
			return nil
		}
	}
	if object.Type() != original.Type() {
		i.reportError(node, fmt.Sprintf("Variable assignment should keep the original data type '%s'", original.Type()))
		return nil
	}
	scope.Update(name, object)
	return object
}
func (i *Interpreter) runAssignSubscript(node *ast.Subscript, original DataType, value DataType, scope *Scope) (DataType, error) {
	index := i.Interpret(node.Index, scope)
	if index == nil {
		return nil, nil
	}
	switch {
	case original.Type() == ARRAY_TYPE && index.Type() == INTEGER_TYPE || index.Type() == PLACEHOLDER_TYPE:
		array := original.(*ArrayType)
		if index.Type() == INTEGER_TYPE {
			idx := index.(*IntegerType).Value
			idx, err := i.checkArrayBounds(array.Elements, idx)
			if err != nil {
				return nil, err
			}
			array.Elements[idx] = value
		} else {
			array.Elements = append(array.Elements, value)
		}
		return array, nil
	case original.Type() == DICTIONARY_TYPE:
		dictionary := original.(*DictionaryType)
		found := false
		for k := range dictionary.Pairs {
			if k.Inspect() == index.Inspect() {
				dictionary.Pairs[k] = value
				found = true
				break
			}
		}
		if !found {
			dictionary.Pairs[index] = value
		}
		return dictionary, nil
	case original.Type() == STRING_TYPE && index.Type() == INTEGER_TYPE && value.Type() == STRING_TYPE:
		str := original.(*StringType)
		idx := index.(*IntegerType).Value
		value := value.(*StringType).Value
		idx, err := i.checkStringBounds(str.Value, idx)
		if err != nil {
			return nil, err
		}
		return &StringType{Value: str.Value[:idx] + value + str.Value[idx+1:]}, nil
	default:
		return nil, fmt.Errorf("Subscript assignment not recognised")
	}
}
func (i *Interpreter) runArray(node *ast.Array, scope *Scope) DataType {
	var result []DataType
	for _, element := range node.List.Elements {
		value := i.Interpret(element, scope)
		result = append(result, value)
	}
	return &ArrayType{Elements: result}
}
func (i *Interpreter) runDictionary(node *ast.Dictionary, scope *Scope) DataType {
	result := map[DataType]DataType{}
	for k, v := range node.Pairs {
		key := i.Interpret(k, scope)
		if key == nil {
			return nil
		}
		value := i.Interpret(v, scope)
		result[key] = value
	}
	return &DictionaryType{Pairs: result}
}
func (i *Interpreter) runIf(node *ast.If, scope *Scope) DataType {
	condition := i.Interpret(node.Condition, scope)
	if i.isTruthy(condition) {
		return i.Interpret(node.Then, NewScopeFrom(scope))
	} else if node.Else != nil {
		return i.Interpret(node.Else, NewScopeFrom(scope))
	} else {
		return NIL
	}
}