package function.builtin import function.ArgumentValidator import function.FunctionNames import function.LispFunction import sexpression.Cons import sexpression.SExpression import sexpression.Symbol import table.ExecutionContext import table.FunctionTable.lookupFunction import table.SymbolTable @FunctionNames("SET") class Set(name: String) : LispFunction() { private val argumentValidator = ArgumentValidator(name).apply { setExactNumberOfArguments(2) setFirstArgumentExpectedType(Symbol::class.java) } override fun call(argumentList: Cons): SExpression { argumentValidator.validate(argumentList) val rest = argumentList.rest as Cons val symbol = argumentList.first val value = rest.first val table = findScopeOfSymbol(symbol) table[symbol.toString()] = value return value } private fun findScopeOfSymbol(symbol: SExpression): SymbolTable { var table: SymbolTable = ExecutionContext.scope while (!isSymbolInTable(symbol, table) && !table.isGlobal()) table = table.parent!! return table } private fun isSymbolInTable(symbol: SExpression, table: SymbolTable) = table.contains(symbol.toString()) companion object { fun set(argumentList: Cons) = lookupFunction("SET")!!.call(argumentList) } }