This commit is contained in:
Tomas Mirchev 2025-10-26 18:54:48 +02:00
parent d8c1dc196d
commit 9a7411420a

View File

@ -29,6 +29,7 @@ const ATTR_TYPE = {
POSITIONAL: "positional", POSITIONAL: "positional",
OPTION: "option", OPTION: "option",
FLAG: "flag", FLAG: "flag",
REST: "rest",
}; };
const CONTEXT = { const CONTEXT = {
POS_COUNT: "pos_count", POS_COUNT: "pos_count",
@ -98,6 +99,11 @@ function parse_spec_entry(raw_entry, context) {
}, },
})[attribute](attribute_value); })[attribute](attribute_value);
}); });
// Set default values
if (!attributes[ATTR.MAP]) {
attributes[ATTR.MAP] = entry.aliases[0]; // alias 0 is always the larger format
}
return attributes; return attributes;
}, },
}[entry.type](); }[entry.type]();
@ -126,12 +132,11 @@ function parse_spec(spec, command = null) {
const context = { const context = {
...ctx_defaults, ...ctx_defaults,
reset() { reset() {
Object.assing(this, defaults); Object.assign(this, ctx_defaults);
}, },
}; };
for (const raw_entry of spec) { for (const raw_entry of spec) {
const entry = parse_spec_entry(raw_entry, context); const entry = parse_spec_entry(raw_entry, context);
console.log(entry.type);
const exit = { const exit = {
[ENTRY.COMMAND]: () => { [ENTRY.COMMAND]: () => {
// Previous command was populated // Previous command was populated
@ -164,26 +169,90 @@ function parse_spec(spec, command = null) {
spec_data[`${cmd_id}.${entry.id}`] = entry; spec_data[`${cmd_id}.${entry.id}`] = entry;
entry.aliases.forEach((alias) => { entry.aliases.forEach((alias) => {
if (entry.attr.type === ATTR_TYPE.POSITIONAL) { if (entry.attr.type === ATTR_TYPE.POSITIONAL) {
spec_aliases[`${cmd_id}.${entry.attr[ATTR.POSITION]}`] = entry.id; const alias_id = `${cmd_id}.${entry.attr[ATTR.POSITION]}`;
} else { spec_aliases[alias_id] = `${cmd_id}.${entry.id}`;
spec_aliases[`${cmd_id}.${alias}`] = entry.id; } else if (
entry.attr.type === ATTR_TYPE.OPTION ||
entry.attr.type === ATTR_TYPE.FLAG
) {
const alias_id = `${cmd_id}.${alias}`;
spec_aliases[alias_id] = `${cmd_id}.${entry.id}`;
} else if (entry.attr.type === ATTR_TYPE.REST) {
const alias_id = `${cmd_id}._rest`;
spec_aliases[alias_id] = `${cmd_id}.${entry.id}`;
} }
}); });
}, },
}[entry.type](); }[entry.type]();
context.reset();
if (exit) break; if (exit) break;
} }
return { spec_data, spec_aliases }; if (command && cmd_id === null) {
throw new Error(`Invalid command: ${command}`);
} }
const r = parse_spec(SPEC, "build"); return {
console.log(JSON.stringify(r, null, 2)); data: spec_data,
aliases: spec_aliases,
cmd_id: command ? cmd_id : null,
};
}
const r = parse_spec(SPEC);
// console.log(JSON.stringify(r, null, 2));
/** /**
* INPUT: build -i tm0:node mybox * @return {<variable_name>:<value>}
*
* spec_aliases[build] -> "0"
* -i -> option/flag short -> spec_aliases["0.i"] -> "1"
*
*/ */
const inspect = (v) => console.log(JSON.stringify(v, null, 2));
function parse_args(spec, args_raw) {
const vars = {};
const args = args_raw.split(" ");
args.shift(); // script name
const cmd = args.shift();
const get_next = () => args[0];
const consume = () => args.shift();
const consume_all = () => args.splice(0);
const cmd_spec = parse_spec(spec, cmd);
const cmd_info = cmd_spec.data[cmd_spec.cmd_id];
let positional_count = 0;
while (args.length > 0) {
const next = get_next();
const [type, next_arg] = (() => {
if (next === "--") {
return [ATTR_TYPE.REST, "_rest"]; // check if spec if expecing a rest arg
} else if (next.startsWith("--")) {
return [ATTR_TYPE.OPTION, next.slice(2)]; // invalid type. use only to know if to search per key or whole string
} else if (next.startsWith("-")) {
return [ATTR_TYPE.FLAG, next.slice(1)]; // same
} else {
return [ATTR_TYPE.POSITIONAL, positional_count++];
}
})();
const alias_id = `${cmd_info.id}.${next_arg}`;
const next_arg_id = cmd_spec.aliases[alias_id];
if (typeof next_arg_id !== "undefined") {
consume();
const next_arg_info = cmd_spec.data[next_arg_id];
if (next_arg_info.attr.type === ATTR_TYPE.OPTION) {
vars[next_arg_info.attr.map] = consume();
} else if (next_arg_info.attr.type === ATTR_TYPE.FLAG) {
vars[next_arg_info.attr.map] = true;
} else if (next_arg_info.attr.type === ATTR_TYPE.REST) {
vars[next_arg_info.attr.map] = consume_all();
} else if (next_arg_info.attr.type === ATTR_TYPE.POSITIONAL) {
vars[next_arg_info.attr.map] = next;
}
} else {
inspect(vars);
throw new Error(`Invalid argument: '${next_arg}'`);
}
}
return vars;
}
const args = "dev build -q -v --image tm0:node mybox -v -- bla1 bla2";
inspect(parse_args(SPEC, args));