/// <summary> /// Compiles a single expression (e.g. part of an expression list) /// </summary> private IFunction CompileExpression(IFunction?previous, Match exprAst, CompilerStackFrame compilerStackFrame, CompilationContext context) { switch (exprAst.Name) { case ElementAST.LiteralExpression: return(new Constant((float)exprAst.Value)); case ElementAST.VariableExpression: context.PushTrace(MakeCallSite(exprAst)); var variable = CompileFunction(exprAst.Text, compilerStackFrame, context); context.PopTrace(); return(variable); case ElementAST.SubExpression: return(previous.Call(exprAst[ElementAST.SubExpressionName].Text, context, MakeCallSite(exprAst))); case ElementAST.CallExpression: // This is called a *lot*, so try to make it high performance: var args = exprAst[ElementAST.CallArguments].Matches; var argList = new IFunction[args.Count]; for (var i = 0; i < argList.Length; i++) { argList[i] = CompileExpressionList(args[i], compilerStackFrame, context); } return(previous.Call(argList, context, MakeCallSite(exprAst))); default: return(context.LogError(9999, $"Unknown expression {exprAst}")); } }
/// <summary> /// Called when another value references this function to compile scoped functions. /// </summary> public IFunction CompileFunction(string name, CompilerStackFrame compilerStackFrame, CompilationContext context) { // First try to get a cached/inputted value... if (!compilerStackFrame.GetLocal(name, out var value)) { // If that fails, look in the list of drivers: if (_drivers != null) { if (_drivers.TryGetValue(name, out var statement)) { if (statement[ElementAST.TypeStatement]) // Check if this statement is a type declaration { var type = FindType(name, context); value = new Constructor(type, context); } else { value = new CustomFunction(this, statement, compilerStackFrame, context, Source); } compilerStackFrame.Add(name, value); } } // If there's no driver list (i.e. during an assignment), then the only output is 'return' // Since there's no other drivers we can assume else if (name == "return") { value = CompileExpressionList(_astAssign, compilerStackFrame, context); compilerStackFrame.Add(name, value); } } // Failing the above, try to find the value including parents in the stack // if we still cannot find a value, try using the captured compilation stack if (value == null && !compilerStackFrame.Get(name, out value) && _capturedCompilationStack != null) { value = Parent.CompileFunction(name, _capturedCompilationStack, context); } if (value == null) { return(context.LogError(7, name)); } return(value.ResolveReturns(context, null)); // TODO: Keep variable information here? }
/// <summary> /// Compiles an expression 'list' (such as chained calls or member accesses) /// </summary> private IFunction CompileExpressionList(Match list, CompilerStackFrame compilerStackFrame, CompilationContext context) => list.Matches.Count == 0 ? CompileExpression(null, list, compilerStackFrame, context) : list.Matches.Aggregate(default(IFunction), (current, exprAst) => CompileExpression(current, exprAst, compilerStackFrame, context));