/// <summary> /// Resolves the items of the ast in the given scope. /// </summary> /// <param name="scope"></param> /// <param name="astAsList"></param> /// <param name="compile"></param> /// <returns></returns> public static List <object> ResolveArgsInScopes(LispScope scope, IEnumerable <object> astAsList, bool compile) { var astWithResolvedValues = new List <object>(); var firstElement = astAsList.FirstOrDefault(); bool?isSpecialForm = null; foreach (var elem in astAsList) { object resolvedElem; if ((isSpecialForm != null && (bool)isSpecialForm) || !IsSymbol(elem)) { resolvedElem = elem; } else { resolvedElem = scope.ResolveInScopes(elem, elem == firstElement); } astWithResolvedValues.Add(resolvedElem); if (isSpecialForm == null) { var firstElem = new LispFunctionWrapper(); object first = null; try { first = astWithResolvedValues.First(); firstElem = ((LispVariant)first).FunctionValue; } catch (LispException) { if (!compile) { throw new LispException("Function \"" + first + "\" not found", scope); } } isSpecialForm = firstElem.IsSpecialForm; } } return(astWithResolvedValues); }
/// <summary> /// Evaluates the given ast. /// </summary> /// <param name="ast">The ast.</param> /// <param name="scope">The scope.</param> /// <returns>The result of ast evaluation.</returns> /// <exception cref="System.Exception">Unexpected macro modus!</exception> public static LispVariant EvalAst(object ast, LispScope scope) { if (ast == null) { return(null); } IList <object> astAsList; if (ast is LispVariant) { var item = (LispVariant)ast; // evaluate the value for the symbol if (item.IsSymbol) { item = new LispVariant(scope.ResolveInScopes(item, false)); return(item); } else if (item.IsList && !item.IsNil) { astAsList = item.ListValue.ToList(); } else { return(item); } } else { astAsList = ((IEnumerable <object>)ast).ToList(); } if (astAsList.Count == 0) { return(new LispVariant(LispType.Nil)); } // is this function a macro ==> process the macro and return if (LispEnvironment.IsMacro(astAsList.First(), scope.GlobalScope)) { // check the macro modus: evaluate or expand or lambda var macro = LispEnvironment.GetMacro(astAsList.First(), scope.GlobalScope); // evaluate macro at run time: if (macro is LispMacroRuntimeEvaluate) { // Example for macro at runtime handling: // // macro definition: // (define-macro-eval my-setf (x value) (setf x value)) // // call (ast): // (my-setf a (+ \"blub\" \"xyz\")) // | | // v v // x value // // Result: // (setf a (+ \"blub\" \"xyz\")) <-- replace formal arguments (as symbol) bool anyMacroReplaced = false; var runtimeMacro = (LispMacroRuntimeEvaluate)macro; var expression = ReplaceFormalArgumentsInExpression(runtimeMacro.FormalArguments, astAsList, runtimeMacro.Expression, scope, ref anyMacroReplaced); return(EvalAst(expression, scope)); } // expand macro at compile time: --> nothing to do at run time ! // code not needed, because code for compile time macros will be removed in ExpandMacro phase //if (macro is LispMacroCompileTimeExpand) //{ // return new LispVariant(); //} throw new Exception("Unexpected macro modus!"); } // for debugging: update the current line number at the current scope var currentToken = ((LispVariant)(astAsList.First())).Token; scope.CurrentToken = currentToken != null ? currentToken : scope.CurrentToken; // resolve values via local and global scope var astWithResolvedValues = ResolveArgsInScopes(scope, astAsList, false); // get first element --> this is the function ! var function = astWithResolvedValues.First(); // normal evaluation... LispFunctionWrapper functionWrapper = ((LispVariant)function).FunctionValue; // trace current function (if tracing is enabled) if (scope.GlobalScope.Tracing) { scope.GlobalScope.Output.WriteLine("--> {0}", astAsList.First()); } // evaluate arguments, but allow recursive lists var arguments = new object[astWithResolvedValues.Count - 1]; for (var i = 1; i < arguments.Length + 1; i++) { //var asContainer = LispUtils.GetAsContainer(astWithResolvedValues[i]); //var needEvaluation = (asContainer != null) && !functionWrapper.IsSpecialForm; //arguments[i - 1] = needEvaluation ? EvalAst(asContainer, scope) : astWithResolvedValues[i]; var needEvaluation = (astWithResolvedValues[i] is IEnumerable <object>) && !functionWrapper.IsSpecialForm; var result = needEvaluation ? EvalAst(astWithResolvedValues[i], scope) : astWithResolvedValues[i]; // process statemens like this: `,@l with l = (1 2 3) LispVariant variant = result as LispVariant; if (variant != null) { if (variant.IsUnQuoted == LispUnQuoteModus.UnQuoteSplicing && variant.IsList) { var lst = variant.ListRef; var newArguments = new object[arguments.Length + lst.Count - 1]; arguments.CopyTo(newArguments, 0); foreach (var elem in lst) { newArguments[i - 1] = elem; i++; } arguments = newArguments; break; } } arguments[i - 1] = result; } // debugger processing var debugger = scope.GlobalScope.Debugger; if (debugger != null && debugger.NeedsBreak(scope, GetPosInfo(astAsList[0]))) { debugger.InteractiveLoop(scope, astAsList); } // call the function with the arguments return(functionWrapper.Function(arguments, scope)); }