public override ErlangValue Evaluate(ErlangProcess process) { ErlangValue last = null; ErlangExpressionBlockExpression def = this; ErlangCompiledModule module = GetModule(); var children = def.GetChildren(); bool doTailCall = false; do { doTailCall = false; // ensure we're reset from the last loop // evaluate each expression Debug.Assert(children.Length > 0); for (int i = 0; !doTailCall && i < children.Length; i++) { var expression = children[i]; string moduleName = null; ErlangFunctionInvocationExpression function = null; ErlangStackFrame tailCallCandidate = null; if (UseTailCalls && i == children.Length - 1 && expression is ErlangFunctionInvocationExpression && expression.IsLastChild) { // if last expression and it's a function invocation function = (ErlangFunctionInvocationExpression)expression; moduleName = function.Module ?? module.Name; tailCallCandidate = process.CallStack.GetTailCallCandidate( moduleName, function.Function, function.Parameters.Length); doTailCall = tailCallCandidate != null; } if (doTailCall) { // evaluate parameters var evaledParams = new ErlangValue[function.Parameters.Length]; for (int j = 0; j < function.Parameters.Length; j++) { var value = function.Parameters[j].Evaluate(process); if (value.Kind == ErlangValueKind.Error) { return(value); } evaledParams[j] = value; } // prepare new frame var newFrame = new ErlangStackFrame(tailCallCandidate.Module, tailCallCandidate.Function, tailCallCandidate.Airity); process.CallStack.RewindForTailCall(newFrame); // find the new definition var group = module.GetFunction(function.Function, evaledParams.Length); def = group.GetFunctionOverload(process, evaledParams); if (def == null) { return(new ErlangAtom("no_such_tailcall_function")); } children = def.GetChildren(); } else { // not a tailcall, just invoke normally last = children[i].Evaluate(process); if (last.Kind == ErlangValueKind.Error) { return(last); // decrease scope? } } } } while (doTailCall); return(last); }
public static ErlangModule Compile(ErlangModuleSyntax syntax) { var module = new ErlangCompiledModule(); // get the module's name var atom = syntax.Attributes.OfType <ErlangAttributeSyntax>().Single(at => at.Name.Text == "module").Parameters.Single().Value as ErlangAtomSyntax; module.Name = atom.Atom.Text; // get visibility var publicFunctions = new HashSet <Tuple <string, int> >(); var exportAll = (from at in syntax.Attributes.OfType <ErlangAttributeSyntax>() where at.Name.Text == "compile" && at.Parameters.Count == 1 let param = at.Parameters[0] where param.Value is ErlangAtomSyntax && ((ErlangAtomSyntax)param.Value).Atom.Text == "export_all" select true).Any(b => b); if (!exportAll) { foreach (var functionReference in from at in syntax.Attributes.OfType <ErlangAttributeSyntax>() where at.Name.Text == "export" && at.Parameters.Count == 1 let param = at.Parameters[0] where param.Value is ErlangListRegularSyntax let list = (ErlangListRegularSyntax)param.Value from item in list.Items where item.Item is ErlangFunctionReferenceSyntax select(ErlangFunctionReferenceSyntax) item.Item) { publicFunctions.Add(Tuple.Create(functionReference.Function.Text, (int)functionReference.Airity.IntegerValue)); } } // compile functions var functionList = new List <ErlangTuple>(); var exportedFunctions = new List <ErlangTuple>(); foreach (var group in syntax.FunctionGroups) { var key = Tuple.Create(group.Name, group.Airity); functionList.Add(new ErlangTuple(new ErlangAtom(group.Name), new ErlangNumber(group.Airity))); var function = (ErlangFunctionGroupExpression)ErlangExpression.Compile(group); function.Module = module; function.IsPublic = exportAll || publicFunctions.Contains(key); if (function.IsPublic) { exportedFunctions.Add(new ErlangTuple(new ErlangAtom(group.Name), new ErlangNumber(group.Airity))); } module.AddFunction(key.Item1, key.Item2, function); } functionList.Add(new ErlangTuple(new ErlangAtom("module_info"), new ErlangNumber(0))); functionList.Add(new ErlangTuple(new ErlangAtom("module_info"), new ErlangNumber(1))); exportedFunctions.Add(new ErlangTuple(new ErlangAtom("module_info"), new ErlangNumber(0))); exportedFunctions.Add(new ErlangTuple(new ErlangAtom("module_info"), new ErlangNumber(1))); module.AllFunctions = new ErlangList(functionList.ToArray()); module.ModuleInfo = new ErlangList( new ErlangTuple(new ErlangAtom("exports"), new ErlangList(exportedFunctions.ToArray())), new ErlangTuple(new ErlangAtom("imports"), new ErlangList()), // always empty. may be removed in future versions new ErlangTuple(new ErlangAtom("attributes"), new ErlangList()), // TODO: populate attributes new ErlangTuple(new ErlangAtom("compile"), new ErlangList()) // TODO: process compile options ); return(module); }