I know this post is long, so I'll keep the important stuff at the top.
In my macro Spotlight Search Prompt, and the other macros that use it, I adopted a different approach, and it works extremely well, except for one outlier situation (which I'll discuss below).
Here's an example:

The format is this.
- One parameter per line
- Format is "name: value" (without the quotes, and the space between the colon and the value is optional).
- If a line is blank, or starts with "--" it is ignored. Leading "--" can be used to "comment out" a line.
- If a parameter allows multiple values, they are separated by commas, as in
jsonAdditionalSearchFields: keyword1, keyword2
This gives me a set of named parameters. Having named parameters allows for optional parameters with ease, and the order of parameters in this list isn't relevant. Also, as mentioned above, it's easy to "comment out" individual lines.
When this is parsed in JavaScript, it becomes something like this:
if (option.title)...
doProcessing(option.dataType);
etc
The one situation this doesn't support is a multi-line parameter. There are ways of handling that, though.
This is very easy to parse in JavaScript, but interestingly enough, it's also easy to parse in KM.

The above example looks for the parameter "customPromptWidth" in the list of parameters, and uses a default value if the parameter doesn't exist. It uses the syntax "^customPromptWidth:", which means "start of a line, immediately followed by "customPromptWidth" and a colon, it effectively ignores leading "--" (or anything else, for that matter).
Details:
I will be happy to provide detailed, documented code to anyone who wants it, and I can write code for anyone who can't quite figure out how to parse the options.
As mentioned, I also have Validation code, if you'd like to see how that works.
For now, here's some examples of parsing the options. If you don't have my complex requirements, the code for parsing the options is as simple as this:
function parseSimpleSpotlightOptions(sspOptions) {
// start with empty options
var options = { };
// split up into multiple lines for easy parsing
var lines = sspOptions.split(/[\r\n]+/);
// process each line
lines.forEach(function(line) {
// skip blank lines, and lines that start with "--"
if (!line.trim() || line.indexOf("--") === 0) return;
// split up into name and value (function is below - it throws errors
// if the line's format is invalid).
var nameValue = getNameValue(line, ":", "option");
var key = nameValue.name;
var value = nameValue.value;
// If there's no value, ignore the option. Not strictly necessary.
if (!value) return;
// if you don't need to do any special parsing for specific values,
// i.e. you don't need to process options that support comma-separated
// values, this is all you need:
switch (key) {
case "title":
case "placeholder":
case "dataType":
case "helpMacroUUID":
case "customButtonSubmitResultButton":
case "customButtonTriggerMacroUUID":
case "saveWindowPositionVariableName":
case "testing":
options[key] = value;
break;
default:
throw Error("Unknown option: '" + key + "'");
}
});
return options;
}
(And yes, @ComplexPoint, there are more compact ways of writing this. Let's try and remember that the people who can understand the compact ways of writing this don't need me to explain how to do it in the first place, OK?
)
Here's what I actually use in Spotlight Search Prompt, which has some complex requirements:
var lines = sspOptions.split(/[\r\n]+/);
lines.forEach(function(line) {
if (!line.trim() || line.indexOf("--") === 0) return;
var nameValue = getNameValue(line, ":", "option");
var key = nameValue.name;
var value = nameValue.value;
if (!value) return;
switch (key) {
case "customPromptWidth":
case "customPromptHeight":
case "customPickListSize":
// These are handled in the macro.
break;
case "title":
case "placeholder":
case "dataType":
case "helpMacroUUID":
case "customButtonSubmitResultButton":
case "customButtonTriggerMacroUUID":
case "saveWindowPositionVariableName":
case "testing":
options[key] = value;
break;
case "customButtonText":
var len = value.length;
if (len > 2 && value.substring(len-2, len-1) === "/") {
options.customButtonText = value.substring(0, value.length-2);
options.customButtonAccessKey = value.substring(value.length-1);
} else {
options.customButtonText = value;
}
break;
case "cancelMacroIfCancelClicked":
case "customButtonEnabledWithoutSelection":
options[key] = value.search(/^(y|yes|t|true|1)$/i) === 0;
break;
case "jsonDisplayField":
options.displayField = value;
break;
case "jsonReturnField":
options.returnField = value;
break;
case "jsonAdditionalSearchFields":
var additionalFields = [];
value.split(/[, ]/).forEach(function(field) {
if (!field) return;
additionalFields.push(field);
});
if (additionalFields.length > 0)
options.additionalSearchFields = additionalFields;
break;
case "jsonStatusLineFields":
var statusLineFields = [];
value.split(/[, ]/).forEach(function(field) {
if (!field) return;
statusLineFields.push(field);
});
if (statusLineFields.length > 0)
options.statusLineFields = statusLineFields;
break;
case "jsonStatusLineTemplate":
options.statusLineTemplate = value;
break;
case "jsonStatusLineFunction":
options.statusLineFunction = value;
break;
default:
throw Error("Unknown option: '" + key + "'");
}
});
I also have a separate function that validates the options, making sure required parameters are specified, and parameters that can't be used together aren't, etc. Available on request.
One last thing - please allow me to geek-out a little.
The thing I really enjoyed about this whole technique, is I could use Unit Testing on it to make sure everything worked correctly, especially when I changed something. And it saved me, many times. Here's an example of the output - I can give details anytime anyone needs them: