private static bool _parseArguments(MacroContext context, out AstExpr call, out AstExpr justEffect, out AstExpr[] args, out AstExpr macroSpec) { /* call(macroRef,...) = call([],macroRef,[false],...); * call([],macroRef,[je],...) = call([],macroRef,[je,context.Call],...); * call([],macroRef,[je,c],...) = { macroId := macroRef.Id; * macroInterpretation := interpretation(macroRef); * call := c; * justEffect := je * } * call([proto(...1)],...2) = call([],from_proto(proto),[false,PCall.Get],[...1],...2); * call([proto(...1) = x],...2) = call([],from_proto(proto),[false,PCall.Set],[...1],...2,[x]); * call([proto(...1),je],...2) = call([],from_proto(proto),[je,PCall.Get],[...1],...2); * call([proto(...1) = x,je],...2) = call([],from_proto(proto),[je,PCall.Set],[...1],...2,[x]); * call([proto(...1),je,c],...2) = call([],from_proto(proto),[je,c],[...1],...2); */ var inv = context.Invocation; justEffect = new AstConstant(inv.File, inv.Line, inv.Column, false); call = PCall.Get.ToExpr(context.Invocation.Position); var invokeSpec = inv.Arguments[0]; var listSpec = invokeSpec as AstListLiteral; if (listSpec == null) { // - Macro reference specified as expression that evaluates to an actual macro reference args = inv.Arguments.Skip(1).ToArray(); macroSpec = invokeSpec; return _parseReference(context, inv.Arguments[0]); } else if (listSpec.Elements.Count == 0) { // - Macro reference specified as expression that evaluates to an actual macro reference // - followed by a list of options AstListLiteral optionsRaw; if (inv.Arguments.Count < 3 || (optionsRaw = inv.Arguments[2] as AstListLiteral) == null) { _errorUsageFullRef(context); args = null; macroSpec = null; return false; } macroSpec = inv.Arguments[1]; //first option: justEffect if (optionsRaw.Elements.Count >= 1) justEffect = optionsRaw.Elements[0]; //second option: call type if (optionsRaw.Elements.Count >= 2) call = optionsRaw.Elements[1]; //args: except first 3 args = inv.Arguments.Skip(3).ToArray(); return _parseReference(context, inv.Arguments[1]); } else { // - Macro reference specified as a prototype // - includes list of options var specProto = listSpec.Elements[0]; PCall protoCall; IList<AstExpr> protoArguments; if ( !_parsePrototype(context, specProto, out protoCall, out protoArguments, out macroSpec)) { args = null; return false; } //first option: justEffect if (listSpec.Elements.Count >= 2) justEffect = listSpec.Elements[1]; //second option: call type call = listSpec.Elements.Count >= 3 ? listSpec.Elements[2] : protoCall.ToExpr(specProto.Position); //args: lift and pass prototype arguments, special care for set var setArgs = protoCall == PCall.Set ? protoArguments.Last() : null; var getArgs = protoCall == PCall.Set ? protoArguments.Take(protoArguments.Count - 1) : protoArguments; // ReSharper disable PossibleMultipleEnumeration // enumerating getArgs multiple times is safe and efficient if (getArgs.Any(a => !_ensureExplicitPlaceholder(context, a))) { args = new AstExpr[] {}; return false; } IEnumerable<AstExpr> getArgsLit; if (getArgs.Any()) { var getArgsLitNode = new AstListLiteral(specProto.File, specProto.Line, specProto.Column); getArgsLitNode.Elements.AddRange(getArgs); getArgsLit = getArgsLitNode.Singleton(); } // ReSharper restore PossibleMultipleEnumeration else { getArgsLit = Enumerable.Empty<AstExpr>(); } IEnumerable<AstExpr> setArgsLit; if (setArgs != null) { if (!_ensureExplicitPlaceholder(context, setArgs)) { args = new AstExpr[] {}; return false; } var lit = new AstListLiteral(setArgs.File, setArgs.Line, setArgs.Column); lit.Elements.Add(setArgs); setArgsLit = lit.Singleton(); } else { setArgsLit = Enumerable.Empty<AstExpr>(); } args = getArgsLit .Append(inv.Arguments.Skip(1)) .Append(setArgsLit).ToArray(); return true; } }