/// <summary> /// Evals the specified lisp code. /// An exception may occure if the lisp code is invalid. /// </summary> /// <param name="lispCode">The lisp code.</param> /// <param name="scope">The scope.</param> /// <param name="moduleName">The module name and path.</param> /// <param name="tracing">if set to <c>true</c> [tracing].</param> /// <param name="onlyMacroExpand">if set to <c>true</c> [macro expanding].</param> /// <param name="nativeItems">The dictionary with native items.</param> /// <returns>The result of the script evaluation</returns> public static LispVariant Eval(string lispCode, LispScope scope = null, string moduleName = null, bool tracing = false, bool onlyMacroExpand = false, Dictionary <string, object> nativeItems = null) { // first create global scope, needed for macro expanding var currentScope = scope ?? LispEnvironment.CreateDefaultScope(); currentScope.ModuleName = moduleName; currentScope.Tracing = tracing; RegisterNativeObjects(nativeItems, currentScope); int offset; string code = LispUtils.DecorateWithBlock(lispCode, out offset); var ast = LispParser.Parse(code, offset, currentScope); #if ENABLE_COMPILE_TIME_MACROS var expandedAst = LispInterpreter.ExpandMacros(ast, currentScope); #else var expandedAst = ast; #endif LispVariant result = null; if (onlyMacroExpand) { result = new LispVariant(expandedAst); } else { result = LispInterpreter.EvalAst(expandedAst, currentScope); } return(result); }
private static IEnumerable <object> ReplaceSymbolWithValueInExpression(LispVariant symbol, object symbolValue, IEnumerable <object> expression, bool macroArgsReplace, ref bool replacedAnything) { var ret = new List <object>(); foreach (var elem in expression) { // is the current element the symbol which should be replaced? --> Yes if (symbol.SymbolCompare(elem)) { IEnumerable <object> l = symbolValue as IEnumerable <object>; if (l != null && macroArgsReplace) { ret.AddRange(l); } else { ret.Add(symbolValue); } replacedAnything = true; } // is it an expression? --> recursive call else if (LispEnvironment.IsExpression(elem)) { IEnumerable <object> temp = ReplaceSymbolWithValueInExpression(symbol, symbolValue, LispEnvironment.GetExpression(elem) /*.ToArray()*/, macroArgsReplace, ref replacedAnything); ret.Add(temp); } // current element is not the symbol which should by replaced ! else { ret.Add(elem); } } return(ret); }
static private Exception CreateInvalidOperationException(string operation, LispVariant l, LispVariant r) { var exception = new LispException(string.Format(NoOperatorForTypes, operation, l.Type, r.Type)); exception.AddTokenInfos(l.Token); return(exception); }
private bool HitsBreakpoint(int lineNo, string moduleName, LispScope scope) { foreach (var breakpoint in Breakpoints) { bool isSameModule = IsSameModule(breakpoint.ModuleName, scope != null ? scope.ModuleName : moduleName); if (isSameModule && (lineNo == breakpoint.LineNo)) { if (breakpoint.Condition.Length > 0 && scope != null) { try { LispVariant result = Lisp.Eval(breakpoint.Condition, scope, scope.ModuleName); return(result.BoolValue); } catch { Output.WriteLine("Error: bad condition for line {0}: {1}", breakpoint.LineNo, breakpoint.Condition); return(false); } } return(true); } } return(false); }
private static object ConvertLispVariantListToListIfNeeded(object something) { LispVariant variant = something as LispVariant; if (variant != null && variant.IsList) { return(variant.ListRef); } return(something); }
private static string ExpandItemForContainer(object item) { if (item is LispVariant) { LispVariant variant = (LispVariant)item; if (variant.IsString) { return(variant.ToStringDebugger()); } } return(item.ToString()); }
/// <summary> /// Gets the additional arguments from the scope. /// </summary> /// <param name="scope">The scope.</param> /// <returns>The result.</returns> public static object[] GetAdditionalArgs(this LispScope scope) { if (scope.ContainsKey(LispEnvironment.ArgsMeta)) { LispVariant variant = scope[LispEnvironment.ArgsMeta] as LispVariant; if (variant != null) { return(variant.ListValue.ToArray()); } } return(new object[0]); }
private static void UpdateFunctionCache(LispVariant elemAsVariant, object value, bool isFirst) { LispVariant valueAsVariant = value as LispVariant; if (isFirst && elemAsVariant != null && valueAsVariant != null && valueAsVariant.IsFunction) { //if (elemAsVariant.CachedFunction != null) //{ // throw new LispException("Cache already set !!!"); //} elemAsVariant.CachedFunction = valueAsVariant; } }
/// <summary> /// See interface. /// </summary> public LispVariant DebuggerLoop(string script, string moduleName, bool tracing = false) { LispVariant result = null; var bRestart = true; while (bRestart) { // save the source code if the script is transfered via command line if (moduleName == LispUtils.CommandLineModule) { CommandLineScript = script; } var globalScope = LispEnvironment.CreateDefaultScope(); globalScope.Input = Input; globalScope.Output = Output; globalScope.Debugger = this; try { result = Lisp.Eval(script, globalScope, moduleName, tracing: tracing); Reset(); } catch (LispStopDebuggerException) { bRestart = false; } catch (Exception exception) { Output.WriteLine("\nException: {0}", exception); string stackInfo = exception.Data.Contains(LispUtils.StackInfo) ? (string)exception.Data[LispUtils.StackInfo] : string.Empty; Output.WriteLine("\nStack:\n{0}", stackInfo); bRestart = InteractiveLoop(this, globalScope, startedFromMain: true); } if (bRestart) { Output.WriteLine("restart program"); // process empty script --> just start interactive loop if (result == null) { bRestart = InteractiveLoop(this, globalScope, startedFromMain: true); } } globalScope.Debugger = null; } return(result); }
/// <summary> /// Resolves the given element in this scope. /// </summary> /// <param name="elem">The element.</param> /// <param name="isFirst">Is the element the first one in the list.</param> /// <returns>Resolved value or null</returns> public object ResolveInScopes(object elem, bool isFirst) { object result; // try to access the cached function value (speed optimization) LispVariant elemAsVariant = elem as LispVariant; if (elemAsVariant != null && elemAsVariant.CachedFunction != null) { return(elemAsVariant.CachedFunction); } var name = elem.ToString(); LispScope foundClosureScope; // first try to resolve in this scope if (TryGetValue(name, out result)) { UpdateFunctionCache(elemAsVariant, result, isFirst); } // then try to resolve in global scope else if (GlobalScope != null && GlobalScope.TryGetValue(name, out result)) { UpdateFunctionCache(elemAsVariant, result, isFirst); } // then try to resolve in closure chain scope(s) else if (IsInClosureChain(name, out foundClosureScope, out result)) { UpdateFunctionCache(elemAsVariant, result, isFirst); } // then try to resolve in scope of loaded modules else if (LispEnvironment.IsInModules(name, GlobalScope)) { result = LispEnvironment.GetFunctionInModules(name, GlobalScope); } else { // activate this code if symbols must be resolved in parameter evaluation --> (println blub) //if (elemAsVariant != null && elemAsVariant.IsSymbol && name != "fuellib") //{ // throw new LispException($"Could not resolve symbol {name}"); //} result = elem; } return(result); }
public static bool EqualOp(LispVariant l, LispVariant r) { if (l.IsNativeObject && r.IsNativeObject) { return(l.NativeObjectValue == r.NativeObjectValue); } if (l.IsSymbol || r.IsSymbol) { return(l.IsSymbol && r.IsSymbol && (l.ToString() == r.ToString())); } if (l.IsBool && r.IsBool) { return(l.BoolValue == r.BoolValue); } if (l.IsNil || r.IsNil) { return(l.IsNil && r.IsNil); } if (l.IsList && r.IsList) { return(l.ListValue.SequenceEqual(r.ListValue)); } if (l.IsUndefined || r.IsUndefined) { return(l.IsUndefined && r.IsUndefined); } if (l.IsString || r.IsString) { return(l.ToString() == r.ToString()); } if (l.IsDouble || r.IsDouble) { return(Math.Abs(l.ToDouble() - r.ToDouble()) < Tolerance); } if (l.IsInt || r.IsInt) { return(l.ToInt() == r.ToInt()); } throw CreateInvalidOperationException("==", l, r); }
/// <summary> /// Evals the specified lisp code. /// All exceptions will be filtered and an error value will be returned. /// </summary> /// <param name="lispCode">The lisp code.</param> /// <param name="moduleName">The current module name.</param> /// <param name="verboseErrorOutput">if set to <c>true</c> [verbose error output].</param> /// <param name="tracing">if set to <c>true</c> [tracing].</param> /// <param name="onlyMacroExpand">if set to <c>true</c> [macro expanding].</param> /// <returns>The result</returns> public static LispVariant SaveEval(string lispCode, string moduleName = null, bool verboseErrorOutput = false, bool tracing = false, bool onlyMacroExpand = false) { LispVariant result; try { result = Eval(lispCode, scope: null, moduleName: moduleName, tracing: tracing, onlyMacroExpand: onlyMacroExpand); } catch (Exception exc) { Console.WriteLine("\nError executing script.\n\n{0} --> line={1} start={2} stop={3} module={4}", exc.Message, exc.Data[LispUtils.LineNo], exc.Data[LispUtils.StartPos], exc.Data[LispUtils.StopPos], exc.Data[LispUtils.ModuleName]); var stackInfo = exc.Data[LispUtils.StackInfo]; Console.WriteLine("\nCallstack:\n{0}", stackInfo != null ? stackInfo : "<not available>"); if (verboseErrorOutput) { Console.WriteLine("\nNative callstack:"); Console.WriteLine("Exception in eval(): {0} \ndata={1}", exc, exc.Data); } result = LispVariant.CreateErrorValue(exc.Message); } return(result); }
private static IEnumerable <object> ReplaceFormalArgumentsInExpression(IEnumerable <object> formalArguments, IList <object> astAsList, IEnumerable <object> expression, LispScope scope, ref bool anyMacroReplaced) { // replace (quoted-macro-args) --> '(<real_args>) int i = 1; bool replaced = false; IEnumerable <object> realArguments = astAsList.Skip(1).ToList(); List <object> quotedRealArguments = new List <object>() { new LispVariant(LispType.Symbol, LispEnvironment.Quote), realArguments }; expression = ReplaceSymbolWithValueInExpression(new LispVariant(LispType.Symbol, "quoted-macro-args"), quotedRealArguments, expression, true, ref replaced); foreach (var formalArgument in formalArguments) { object value; if (astAsList[i] is IEnumerable <object> ) { value = ExpandMacros(astAsList[i], scope, ref anyMacroReplaced); if (value is LispVariant) { var vairantValue = value as LispVariant; if (vairantValue.IsList) { value = vairantValue.ListValue; } } } else { value = new LispVariant(astAsList[i]); } expression = ReplaceSymbolWithValueInExpression((LispVariant)formalArgument, value, expression, false, ref anyMacroReplaced); i++; } return(expression); }
private static int ParseTokens(string moduleName, IList <LispToken> tokens, int startIndex, ref object parseResult, bool isToplevel) { int i; List <object> current = null; var listStack = new Stack <List <object> >(); for (i = startIndex; i < tokens.Count; i++) { LispToken token = tokens[i]; if (token.Type == LispTokenType.ListStart) { current = new List <object>(); listStack.Push(current); } else if (token.Type == LispTokenType.ListEnd) { var temp = current; listStack.Pop(); if (listStack.Count > 0) { listStack.Peek().Add(temp); current = listStack.Peek(); } else { if (isToplevel && i + 1 < tokens.Count && !OnlyCommentTokensFrom(tokens, i + 1)) { throw new LispException(BracketsOutOfBalanceOrUnexpectedScriptCode, token, moduleName); } parseResult = current; return(i); } } else if (token.Type == LispTokenType.Quote || token.Type == LispTokenType.QuasiQuote) { var quote = new List <object>(); quote.Add(new LispVariant(LispType.Symbol, token.Type == LispTokenType.Quote ? LispEnvironment.Quote : LispEnvironment.Quasiquote)); object quotedList = null; i = ParseTokens(moduleName, tokens, i + 1, ref quotedList, isToplevel: false); quote.Add(quotedList); if (current != null) { current.Add(quote); } } else if (token.Type == LispTokenType.UnQuote || token.Type == LispTokenType.UnQuoteSplicing) { var unquote = new List <object>(); LispUnQuoteModus unquotedModus = token.Type == LispTokenType.UnQuote ? LispUnQuoteModus.UnQuote : LispUnQuoteModus.UnQuoteSplicing; unquote.Add(new LispVariant(LispType.Symbol, token.Type == LispTokenType.UnQuote ? LispEnvironment.UnQuote : LispEnvironment.UnQuoteSplicing)); object quotedList = null; i = ParseTokens(moduleName, tokens, i + 1, ref quotedList, isToplevel: false); unquote.Add(quotedList); if (current != null) { current.Add(unquote); } else { parseResult = unquote; return(i); } } else if (token.Type == LispTokenType.Comment) { // just ignore comment } else { if (!isToplevel && current == null) { parseResult = new LispVariant(token); return(i); } if (current == null) { throw new LispException(UnexpectedToken, token, moduleName); } current.Add(new LispVariant(token)); } } if (isToplevel && tokens.Count > 0) { LispToken token = tokens.Last(); throw new LispException(BracketsOutOfBalance, token, moduleName); } parseResult = current; return(i); }
/// <summary> /// Processing of the interactive loop of the debugger. /// </summary> /// <param name="debugger">The debugger.</param> /// <param name="initialTopScope">The initial top scope.</param> /// <param name="startedFromMain">if set to <c>true</c> [started from main].</param> /// <param name="tracing">if set to <c>true</c> tracing is enabled.</param> /// <returns>True if program should be restarted.</returns> /// <exception cref="LispStopDebuggerException"></exception> /// <exception cref="CsLisp.LispStopDebuggerException"></exception> public static bool InteractiveLoop(LispDebugger debugger = null, LispScope initialTopScope = null, bool startedFromMain = false, bool tracing = false) { string interactiveScript = string.Empty; startedFromMain = startedFromMain || debugger == null; if (debugger == null) { debugger = new LispDebugger(); } var globalScope = initialTopScope != null ? initialTopScope.GlobalScope : LispEnvironment.CreateDefaultScope(); // do not switch off tracing if already enabled if (!globalScope.Tracing) { globalScope.Tracing = tracing; } var topScope = initialTopScope != null ? initialTopScope : globalScope; var currentScope = topScope; var bContinueWithNextStatement = false; var bRestart = false; do { debugger.Output.Write(debugger != null ? DbgPrompt : Prompt); // Warning: // QProcess and .NET >3.5 does not work correclty reading from input !!! // see: http://www.qtcentre.org/threads/62415-QProcess-not-communicating-with-net-framework-gt-3-5 // ==> CsLisp is now using .NET 3.5 ! var cmd = debugger.Input.ReadLine(); cmd = cmd != null?cmd.Trim() : null; if (cmd == null || cmd.Equals("exit") || cmd.Equals("quit") || cmd.Equals("q")) { bContinueWithNextStatement = true; bRestart = false; if (!startedFromMain) { throw new LispStopDebuggerException(); } } else if (cmd.Equals("help") || cmd.Equals("h")) { ShowInteractiveCmds(debugger.Output); } else if (cmd.Equals("about")) { LispUtils.ShowAbout(debugger.Output); } else if (cmd.Equals("funcs")) { globalScope.DumpFunctions(); } else if (cmd.Equals("macros")) { globalScope.DumpMacros(); } else if (cmd.Equals("builtins")) { globalScope.DumpBuiltinFunctions(); } else if (cmd.StartsWith("doc")) { var items = cmd.Split(' '); if (items.Length > 1) { string docCmd = "(doc '" + items[1] + ")"; LispVariant result = Lisp.Eval(docCmd, currentScope, currentScope.ModuleName); debugger.Output.WriteLine("{0}", result); } else { globalScope.DumpBuiltinFunctionsHelp(); } } else if (cmd.StartsWith("searchdoc")) { var items = cmd.Split(' '); if (items.Length > 1) { string docCmd = "(searchdoc '" + items[1] + ")"; LispVariant result = Lisp.Eval(docCmd, currentScope, currentScope.ModuleName); debugger.Output.WriteLine("{0}", result); } else { globalScope.DumpBuiltinFunctionsHelp(); } } else if (cmd.Equals("modules")) { globalScope.DumpModules(); } else if (cmd.StartsWith("clear")) { ClearBreakpoints(debugger, cmd); } else if (cmd.Equals("stack") || cmd.StartsWith("k")) { topScope.DumpStack(currentScope.GetCallStackSize()); } else if (cmd.Equals("code") || cmd.StartsWith("c")) { var script = string.Empty; var moduleName = currentScope.ModuleName; if (moduleName == null) { script = interactiveScript; } else { script = moduleName.StartsWith(LispEnvironment.EvalStrTag) ? moduleName.Substring(LispEnvironment.EvalStrTag.Length + moduleName.IndexOf(":", LispEnvironment.EvalStrTag.Length)) : LispUtils.ReadFileOrEmptyString(moduleName); } // use the script given on command line if no valid module name was set if (string.IsNullOrEmpty(script)) { script = debugger.CommandLineScript; } ShowSourceCode(debugger, script, currentScope.ModuleName, currentScope.CurrentLineNo); } else if (cmd.StartsWith("list") || cmd.StartsWith("t")) { debugger.ShowBreakpoints(); } else if (cmd.StartsWith("break ") || cmd.StartsWith("b ")) { AddBreakpoint(debugger, cmd, currentScope.ModuleName); } else if (cmd.Equals("up") || cmd.StartsWith("u")) { if (currentScope.Next != null) { currentScope = currentScope.Next; } } else if (cmd.Equals("down") || cmd.StartsWith("d")) { if (currentScope.Previous != null) { currentScope = currentScope.Previous; } } else if (cmd.Equals("step") || cmd.Equals("s")) { debugger.DoStep(currentScope); bContinueWithNextStatement = true; } else if (cmd.Equals("over") || cmd.Equals("v")) { debugger.DoStepOver(currentScope); bContinueWithNextStatement = true; } else if (cmd.Equals("out") || cmd.Equals("o")) { debugger.DoStepOut(currentScope); bContinueWithNextStatement = true; } else if (cmd.Equals("run") || cmd.Equals("r")) { debugger.DoRun(); bContinueWithNextStatement = true; } else if (cmd.Equals("locals") || cmd.StartsWith("l")) { currentScope.DumpVars(); } else if (cmd.Equals("globals") || cmd.StartsWith("g")) { globalScope.DumpVars(); } else if (cmd.Equals("restart")) { bContinueWithNextStatement = true; bRestart = true; } else if (cmd.Equals("version") || cmd.Equals("ver")) { LispUtils.ShowVersion(debugger.Output); } else { try { LispVariant result = Lisp.Eval(cmd, currentScope, currentScope.ModuleName); debugger.Output.WriteLine("result={0}", result); interactiveScript += cmd + '\n'; } catch (Exception ex) { debugger.Output.WriteLine("Exception: " + ex.Message); } } } while (!bContinueWithNextStatement); return(bRestart); }
/// <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)); }
private static Tuple <string, string> Compile(IEnumerable <object> ast, LispScope globalScope, string shift, string saveReturn, string scopeName) { string code = string.Empty; string functions = string.Empty; var astWithResolvedValues = LispInterpreter.ResolveArgsInScopes(globalScope, ast, true); // do --> sequence, d. h. ignorieren // defn --> funktion deklarieren // def --> variable deklarieren ==> LispVariant a = value; // quote // setf --> variable zuweisen // while // lambda // if // weitere special forms speziell behandeln... // --> and, or, apply, quasiquote, definemacro object first = null; string args = string.Empty; string separator = ", "; string closeStatement = ""; for (int i = 0; i < astWithResolvedValues.Count; i++) { var elem = astWithResolvedValues[i]; if (first == null) { first = elem; var func = (LispVariant)first; if (func.IsSymbol) { // process normal function call string cast = GetFunctionCast(astWithResolvedValues.Count - i - 1); code += shift + "/*local_func*/((" + cast + ")" + ast.First() + ")("; separator = ", "; closeStatement = ", " + scopeName + ")"; } else { LispFunctionWrapper function = ((LispVariant)first).FunctionValue; if (function.Function == LispEnvironment.def_form) { var compileResult = Compile(astWithResolvedValues[i + 2], globalScope, string.Empty, null, scopeName); // TODO --> ggf. nur einfache datentypen dekorieren !? siehe bei arg code += shift + "/*def_form*/LispVariant " + astWithResolvedValues[i + 1] + " = new LispVariant( (object)" + compileResult.Item1 + ")"; functions += compileResult.Item2; separator = ""; closeStatement = ""; i += 2; } else if (function.Function == LispEnvironment.defn_form) { // process normal function call functions += shift + "/*defn*/private static LispVariant " + astWithResolvedValues[i + 1] + "("; var argStrg = string.Empty; foreach (var arg in (IEnumerable <object>)astWithResolvedValues[i + 2]) { if (argStrg.Length > 0) { argStrg += ", "; } argStrg += "LispVariant " + arg; } functions += argStrg + ", LispScope " + scopeName + ")\n"; functions += shift + "{\n"; functions += shift + "LispVariant __return__;\n"; functions += Compile(astWithResolvedValues[i + 3], globalScope, string.Empty, "__return__", scopeName).Item1; functions += shift + ";\n"; functions += shift + "\nreturn __return__;\n"; functions += shift + "}\n"; separator = ""; closeStatement = ""; i += 3; } else if (function.Function == LispEnvironment.quote_form) { // (quote (1 2 3)) --> new object[] { 1, 2, 3 } // (quote 'something) --> new LispVariant(LispType.Symbol, "something") code += "/*quote*/new object[] { "; var temp = string.Empty; if (astWithResolvedValues[i + 1] is IEnumerable <object> ) { var aList = (IEnumerable <object>)astWithResolvedValues[i + 1]; foreach (var item in aList) { if (temp.Length > 0) { temp += ", "; } temp += "new LispVariant( "; temp += Compile(item, globalScope, string.Empty, null, scopeName).Item1; temp += " )"; } } else { LispVariant variant = (LispVariant)astWithResolvedValues[i + 1]; temp += "new LispVariant( LispType.Symbol, \""; temp += variant.ToString(); temp += "\" )"; } code += temp + " }"; i++; } else if (function.Function == LispEnvironment.quasiquote_form) { // (quasiquote (1 2 3 ,a)) --> new object[] { 1, 2, 3, 42 } // TODO korrekt implementieren !!! code += "/*quasiquote*/new object[] { "; var aList = (IEnumerable <object>)astWithResolvedValues[i + 1]; var temp = string.Empty; foreach (var item in aList) { if (temp.Length > 0) { temp += ", "; } temp += "new LispVariant( "; temp += Compile(item, globalScope, string.Empty, null, scopeName).Item1; temp += " )"; } code += temp + " }"; i++; } else if (function.Function == LispEnvironment.do_form) { // process do_form separator = "\n"; closeStatement = ""; int j; string temp = "{ /*do*/\n"; for (j = i + 1; j < astWithResolvedValues.Count; j++) { var compileResult = Compile(astWithResolvedValues[j], globalScope, shift, saveReturn, scopeName); functions += compileResult.Item2; temp += compileResult.Item1; if (temp.Length > 0) { temp += ";\n"; } } i = j; code += temp + "/*do*/ }\n"; } else if (function.Function == LispEnvironment.if_form) { code += "if( "; code += Compile(astWithResolvedValues[i + 1], globalScope, string.Empty, null, scopeName).Item1; code += ".ToBool() )\n{\n"; code += Compile(astWithResolvedValues[i + 2], globalScope, " ", null, scopeName).Item1; code += ";\n}\n"; if (astWithResolvedValues.Count > i + 3) { code += "else\n{\n"; code += Compile(astWithResolvedValues[i + 3], globalScope, " ", null, scopeName).Item1; code += ";\n}\n"; i += 3; } else { i += 2; } } else if (function.Function == LispEnvironment.while_form) { code += "while( "; code += Compile(astWithResolvedValues[i + 1], globalScope, string.Empty, null, scopeName).Item1; code += ".ToBool() )\n{\n"; code += Compile(astWithResolvedValues[i + 2], globalScope, " ", null, scopeName).Item1; code += ";}\n"; i += 2; } else if (function.Function == LispEnvironment.setf_form) { code += Compile(astWithResolvedValues[i + 1], globalScope, string.Empty, null, scopeName).Item1; code += " = new LispVariant("; code += Compile(astWithResolvedValues[i + 2], globalScope, string.Empty, null, scopeName).Item1; code += ")"; i += 2; } else if (function.Function == LispEnvironment.fn_form) { code += "/*fn*/new LispVariant( LispType.Function, new LispFunctionWrapper( ("; var argNames = (IEnumerable <object>)astWithResolvedValues[i + 1]; var temp = string.Empty; foreach (var name in argNames) { if (temp.Length > 0) { temp += ", "; } temp += "LispVariant " + name; } code += temp; code += ", LispScope _scope) => { LispVariant __ret__; "; code += Compile(astWithResolvedValues[i + 2], globalScope, string.Empty, "__ret__", "_scope").Item1; code += "; return __ret__; }, /*signature*/string.Empty, /*documentation*/string.Empty, false ) )"; i += 2; } else { // process normal function call code += shift; if (!string.IsNullOrEmpty(saveReturn)) { code += saveReturn + " = "; } code += "/*func*/LispEnvironment." + ResolveFunction(ast.First(), globalScope) + "( new object[] { "; separator = ", "; closeStatement = " }, " + scopeName + ")"; } } } else { if (args.Length > 0) { args += separator; } if (elem is IEnumerable <object> ) { var compileResult = Compile((IEnumerable <object>)elem, globalScope, shift, saveReturn, scopeName); args += compileResult.Item1; functions += compileResult.Item2; } else { // decorate simple data types with LispVariant var temp = (LispVariant)elem; var elemType = temp.Type; if (elemType == LispType.Bool || elemType == LispType.Int || elemType == LispType.Double || elemType == LispType.String) { args += "/*arg*/new LispVariant( (object)" + temp.ToStringCompiler() + " )"; } else { args += "/*arg*/" + elem; } } } } code += args + closeStatement; return(new Tuple <string, string>(code, functions)); }