/parsing/options.d

http://github.com/wilkie/djehuty · D · 479 lines · 383 code · 60 blank · 36 comment · 133 complexity · 20999452578447cdf63024a16cccabed MD5 · raw file

  1. /*
  2. * options.d
  3. *
  4. * This module implements a mechanism to read in command line options.
  5. *
  6. * Author: Dave Wilkinson
  7. * Originated: September 28th, 2009
  8. *
  9. */
  10. module parsing.options;
  11. import core.util;
  12. import core.string;
  13. import core.definitions;
  14. import core.arguments;
  15. import core.main;
  16. public import _ConsoleIO = io.console;
  17. template BuildArgumentRetrieval(T) {
  18. //pragma(msg, T.stringof);
  19. static if (is(T == char)) {
  20. const char[] BuildArgumentRetrieval = `(param)[0];`;
  21. }
  22. else static if (IsSigned!(T)) {
  23. const char[] BuildArgumentRetrieval = `cast(` ~ T.stringof ~ `)param.toInt();`;
  24. }
  25. else {
  26. const char[] BuildArgumentRetrieval = `param;`;
  27. }
  28. }
  29. template BuildArgumentRetrieve(uint argidx, uint idx, list...) {
  30. static if (idx >= list.length) {
  31. const char[] BuildArgumentRetrieve = ``;
  32. }
  33. else static if (list[idx].stringof[0] != '"') {
  34. const char[] BuildArgumentRetrieve = `param = getParameter();`
  35. ~ "\n\t\t\t\t"
  36. ~ `if (param is null) { onError(token); return; } `
  37. ~ "\n\t\t\t\t"
  38. ~ list[idx].stringof ~ ` arg` ~ argidx.stringof[0..$-1] ~ ` = `
  39. ~ BuildArgumentRetrieval!(list[idx])
  40. ~ "\n\t\t\t\t"
  41. ~ BuildArgumentRetrieve!(argidx+1,idx+2, list);
  42. }
  43. else {
  44. // no more parameters
  45. const char[] BuildArgumentRetrieve = ``;
  46. }
  47. }
  48. template BuildOpParams(uint argidx, uint idx, list...) {
  49. static if (idx >= list.length) {
  50. const char[] BuildOpParams = ``;
  51. }
  52. else static if (argidx == 0) {
  53. static if (list[idx].stringof[0] != '"') {
  54. const char[] BuildOpParams = `arg0` ~ BuildOpParams!(argidx+1, idx+2, list);
  55. }
  56. else {
  57. const char[] BuildOpParams = ``;
  58. }
  59. }
  60. else {
  61. static if (list[idx].stringof[0] != '"') {
  62. const char[] BuildOpParams = `, arg` ~ argidx.stringof[0..$-1] ~ BuildOpParams!(argidx+1, idx+2, list);
  63. }
  64. else {
  65. const char[] BuildOpParams = ``;
  66. }
  67. }
  68. }
  69. template RebuildOpName(char[] name, uint idx = 0) {
  70. static if (idx >= name.length || name == "" || name[idx] == ',' || name[idx] == ' ') {
  71. const char[] RebuildOpName = Capitalize!(name[0..idx]);
  72. }
  73. else static if (name[idx] == '-') {
  74. const char[] RebuildOpName = Capitalize!(name[0..idx]) ~ RebuildOpName!(name[idx+1..$], 0);
  75. }
  76. else {
  77. const char[] RebuildOpName = RebuildOpName!(name, idx + 1);
  78. }
  79. }
  80. template BuildOpName(uint idx, list...) {
  81. const char[] BuildOpName = `on` ~ RebuildOpName!(list[idx]) ~ `(` ~ BuildOpParams!(0, idx+2, list) ~ `);`;
  82. }
  83. template BuildCaseList(char[] optionstring, uint pos = 0) {
  84. static if (pos >= optionstring.length) {
  85. const char[] BuildCaseList = `case ` ~ "`" ~ optionstring ~ "`" ~ `:
  86. `;
  87. }
  88. else static if (optionstring[pos] == ',' || optionstring[pos] == ' ') {
  89. static if (Trim!(optionstring[0..pos]) == "") {
  90. const char[] BuildCaseList = BuildCaseList!(optionstring[pos+1..$], 0);
  91. }
  92. else {
  93. const char[] BuildCaseList = `case ` ~ "`" ~ Trim!(optionstring[0..pos]) ~ "`" ~ `:
  94. ` ~ BuildCaseList!(optionstring[pos+1..$], 0);
  95. }
  96. }
  97. else {
  98. const char[] BuildCaseList = BuildCaseList!(optionstring, pos + 1);
  99. }
  100. }
  101. template BuildArgumentItem(uint idx, list...) {
  102. static if (idx >= list.length) {
  103. const char[] BuildArgumentItem = ``;
  104. }
  105. else static if (list[idx].stringof[0] == '"') {
  106. //pragma(msg, idx.stringof ~ "::" ~ list[idx].stringof);
  107. const char[] BuildArgumentItem =
  108. BuildCaseList!(list[idx])
  109. ~ BuildArgumentRetrieve!(0, idx+2, list)
  110. ~ BuildOpName!(idx, list)
  111. ~ "\n\t\t\t\t" ~ `break;
  112. ` ~ BuildArgumentItem!(idx+2, list);
  113. }
  114. else {
  115. const char[] BuildArgumentItem = "" ~ BuildArgumentItem!(idx+2, list);
  116. }
  117. }
  118. // assumptions: 'token' is a string to check
  119. template BuildArgumentParser(list...) {
  120. const char[] BuildArgumentParser = `
  121. switch(token) {
  122. ` ~ BuildArgumentItem!(0, list) ~ `
  123. default:
  124. // no option found
  125. tokenFound = false;
  126. break;
  127. }
  128. `;
  129. }
  130. template BuildUsagestring(char[] name, uint idx = 0) {
  131. static if (idx >= name.length) {
  132. const char[] BuildUsagestring = Trim!(name[0..idx]);
  133. }
  134. else static if (name[idx] == ',') {
  135. const char[] BuildUsagestring = Trim!(name[0..idx]) ~ ", -" ~ BuildUsagestring!(name[idx+1..$], 0);
  136. }
  137. else {
  138. const char[] BuildUsagestring = BuildUsagestring!(name, idx + 1);
  139. }
  140. }
  141. template BuildUsageParameterList(uint idx, list...) {
  142. static if (idx >= list.length) {
  143. const char[] BuildUsageParameterList = ` `;
  144. }
  145. else static if (list[idx].stringof[0] == '"') {
  146. const char[] BuildUsageParameterList = ` `;
  147. }
  148. else {
  149. static if (list[idx].stringof == "char[]") {
  150. // {string} is prettier, imho
  151. const char[] BuildUsageParameterList = ` {string}` ~ BuildUsageParameterList!(idx+2, list);
  152. }
  153. else {
  154. const char[] BuildUsageParameterList = ` {` ~ list[idx].stringof ~ `}` ~ BuildUsageParameterList!(idx+2, list);
  155. }
  156. }
  157. }
  158. template BuildUsageParameterPretty(char[] foo) {
  159. const char[] BuildUsageParameterPretty = " "[0..(11 - foo.length)] ~ foo;
  160. }
  161. template BuildUsageParameterDescList(uint idx, list...) {
  162. static if (idx >= list.length) {
  163. const char[] BuildUsageParameterDescList = ``;
  164. }
  165. else static if (list[idx].stringof[0] == '"') {
  166. const char[] BuildUsageParameterDescList = ``;
  167. }
  168. else {
  169. static if (list[idx].stringof == "char[]") {
  170. // {string} is prettier, imho
  171. const char[] BuildUsageParameterDescList = ` ` ~ BuildUsageParameterPretty!(`{string}`) ~ ` - ` ~ list[idx+1] ~ `\n` ~ BuildUsageParameterDescList!(idx+2, list);
  172. }
  173. else {
  174. const char[] BuildUsageParameterDescList = ` ` ~ BuildUsageParameterPretty!(`{` ~ list[idx].stringof ~ `}`) ~ ` - ` ~ list[idx+1] ~ `\n` ~ BuildUsageParameterDescList!(idx+2, list);
  175. }
  176. }
  177. }
  178. template BuildUsageDesc(uint idx, list...) {
  179. static if (idx >= list.length) {
  180. const char[] BuildUsageDesc = ``;
  181. }
  182. else {
  183. const char[] BuildUsageDesc = `_ConsoleIO.Console.putln("-` ~ BuildUsagestring!(list[idx]) ~ BuildUsageParameterList!(idx + 2, list) ~ `: ` ~ list[idx+1] ~ `");
  184. _ConsoleIO.Console.putln("` ~ BuildUsageParameterDescList!(idx + 2, list) ~ `");
  185. `;
  186. }
  187. }
  188. template BuildUsageItem(uint idx, list...) {
  189. static if (idx >= list.length) {
  190. const char[] BuildUsageItem = ``;
  191. }
  192. else static if (list[idx].stringof[0] == '"') {
  193. const char[] BuildUsageItem =
  194. BuildCaseList!(list[idx])
  195. ~ BuildUsageDesc!(idx, list)
  196. ~ "\n\t\t\t\t" ~ `break;
  197. ` ~ BuildUsageItem!(idx+2, list);
  198. }
  199. else {
  200. const char[] BuildUsageItem = "" ~ BuildUsageItem!(idx+2, list);
  201. }
  202. }
  203. // Assumes 'option' contains the option to view
  204. template BuildUsagePrinter(list...) {
  205. const char[] BuildUsagePrinter = `
  206. switch(option) {
  207. ` ~ BuildUsageItem!(0, list) ~ `
  208. default:
  209. // no option
  210. break;
  211. }
  212. `;
  213. }
  214. template BuildOpSimpleName(char[] name, uint idx = 0) {
  215. static if (idx >= name.length) {
  216. const char[] BuildOpSimpleName = Trim!(name[0..idx]);
  217. }
  218. else static if (name[idx] == ',') {
  219. const char[] BuildOpSimpleName = Trim!(name[0..idx]);
  220. }
  221. else {
  222. const char[] BuildOpSimpleName = BuildOpSimpleName!(name, idx + 1);
  223. }
  224. }
  225. template BuildOpSortableName(char[] name, uint pos = 0) {
  226. static if (pos >= name.length) {
  227. const char[] BuildOpSortableName = ` ` ~ BuildOpSimpleName!(name);
  228. }
  229. else static if (name[pos] == '-') {
  230. const char[] BuildOpSortableName = BuildOpSortableName!(name, pos + 1);
  231. }
  232. else {
  233. const char[] BuildOpSortableName = name[pos..$] ~ ` ` ~ BuildOpSimpleName!(name);
  234. }
  235. }
  236. template BuildOptionArray(uint idx, list...) {
  237. static if (idx >= list.length) {
  238. const char[] BuildOptionArray = ``;
  239. }
  240. else static if (list[idx].stringof[0] == '"') {
  241. const char[] BuildOptionArray = `"` ~ BuildOpSortableName!(list[idx]) ~ `",
  242. ` ~ BuildOptionArray!(idx + 2, list);
  243. }
  244. else {
  245. const char[] BuildOptionArray = BuildOptionArray!(idx + 2, list);
  246. }
  247. }
  248. template BuildUsagePrinterAllDescPretty(char[] foo) {
  249. static if (foo.length >= 40) {
  250. const char[] BuildUsagePrinterAllDescPretty = foo;
  251. }
  252. else {
  253. const char[] BuildUsagePrinterAllDescPretty = foo ~ " "[0..35 - foo.length] ;
  254. }
  255. }
  256. template BuildUsagePrinterAllDesc(uint idx, list...) {
  257. static if (idx >= list.length) {
  258. const char[] BuildUsagePrinterAllDesc = ``;
  259. }
  260. else static if (list[idx].stringof[0] == '"') {
  261. const char[] BuildUsagePrinterAllDesc = `_ConsoleIO.Console.putln(" `
  262. ~ BuildUsagePrinterAllDescPretty!(Trim!(list[idx] ~ BuildUsageParameterList!(idx+2, list)))
  263. ~ ` - `
  264. ~ list[idx + 1]
  265. ~ `");`;
  266. }
  267. else {
  268. const char[] BuildUsagePrinterAllDesc = "" ~ BuildUsagePrinterAllDesc!(idx+2, list);
  269. }
  270. }
  271. template BuildUsagePrinterAllItem(uint idx, list...) {
  272. static if (idx >= list.length) {
  273. const char[] BuildUsagePrinterAllItem = ``;
  274. }
  275. else static if (list[idx].stringof[0] == '"') {
  276. const char[] BuildUsagePrinterAllItem =
  277. BuildCaseList!(list[idx])
  278. ~ BuildUsagePrinterAllDesc!(idx, list)
  279. ~ "\n\t\t\t\t" ~ `break;
  280. ` ~ BuildUsagePrinterAllItem!(idx+2, list);
  281. }
  282. else {
  283. const char[] BuildUsagePrinterAllItem = "" ~ BuildUsagePrinterAllItem!(idx+2, list);
  284. }
  285. }
  286. template BuildUsagePrinterAll(list...) {
  287. const char[] BuildUsagePrinterAll = `
  288. string[] options = [
  289. ` ~ Trim!(BuildOptionArray!(0, list))[0..$-1] ~ `
  290. ];
  291. options.sort;
  292. foreach(_opt; options) {
  293. string _str = _opt.dup;
  294. int pos = _str.findReverse(" ");
  295. string option = _str[pos+1.._str.length];
  296. switch (option) {
  297. ` ~ BuildUsagePrinterAllItem!(0, list) ~ `
  298. default:
  299. break;
  300. }
  301. }
  302. `;
  303. }
  304. abstract class OptionParser {
  305. this() {
  306. parse();
  307. }
  308. void parse() {
  309. }
  310. void showUsage() {
  311. }
  312. void printUsage(string option) {
  313. }
  314. void onError(string option) {
  315. // Default handler will print out the correct usage of the
  316. // option and exit
  317. _ConsoleIO.Console.putln("Error in syntax for option: -", option);
  318. _ConsoleIO.Console.putln();
  319. printUsage(option);
  320. Djehuty.application.exit(0);
  321. }
  322. }
  323. template Options(list...) {
  324. // print out list of options
  325. override string toString() {
  326. return "";
  327. }
  328. override void showUsage() {
  329. // Traditional header
  330. _ConsoleIO.Console.putln("OVERVIEW: Application Name - Version - 0.0.0");
  331. _ConsoleIO.Console.putln();
  332. _ConsoleIO.Console.putln("USAGE: foo ");
  333. _ConsoleIO.Console.putln();
  334. _ConsoleIO.Console.putln("OPTIONS:");
  335. // Followed by an alphabetical listing of options and their usage
  336. //pragma(msg,BuildUsagePrinterAll!(list));
  337. mixin(BuildUsagePrinterAll!(list));
  338. }
  339. override void printUsage(string option) {
  340. char[] token;
  341. //pragma(msg,BuildUsagePrinter!(list));
  342. mixin(BuildUsagePrinter!(list));
  343. }
  344. override void parse() {
  345. Arguments args = Arguments.instance;
  346. // _ConsoleIO.Console.putln(args.length);
  347. // foreach(var; args) {
  348. // _ConsoleIO.Console.putln(var);
  349. // }
  350. char[] token;
  351. string param;
  352. for (uint i; i < args.length; ) {
  353. string arg;
  354. void pullArgument() {
  355. arg = args.peekAt(i);
  356. i++;
  357. }
  358. pullArgument();
  359. if (arg[0] == '-') {
  360. // it is an option
  361. for (uint c = 1; c < arg.length; c++) {
  362. string getParameter() {
  363. string ret;
  364. if (arg is null) {
  365. return null;
  366. }
  367. if (c >= arg.length - 1) {
  368. pullArgument();
  369. ret = arg;
  370. }
  371. else {
  372. ret = arg.substring(c+1);
  373. }
  374. if (ret !is null && ret.length > 0 && (ret[0] == '"' || ret[0] == '\'')) {
  375. char lookingFor = ret[0];
  376. // we have to look for the end of the string argument
  377. while (!(ret[ret.length - 1] == lookingFor && (ret.length == 1 || ret[ret.length - 2] != '\\'))) {
  378. pullArgument();
  379. if (arg is null) {
  380. onError(token);
  381. return null;
  382. }
  383. ret ~= " ";
  384. ret ~= arg;
  385. }
  386. // Replace escaped characters
  387. char[] findStr = `\` ~ [lookingFor];
  388. int pos = ret.find(findStr);
  389. while (pos != -1) {
  390. ret = (ret[0..pos] ~ [lookingFor] ~ ret[pos+2..ret.length]);
  391. pos = ret.find(findStr, pos+1);
  392. }
  393. // good
  394. ret = ret.substring(1, ret.length - 2);
  395. }
  396. if (arg !is null) {
  397. c = arg.length;
  398. }
  399. return ret;
  400. }
  401. dchar chr = arg[c];
  402. if (token.length == 0 && chr == '-' && c != 1) {
  403. // incorrect
  404. onError(token);
  405. return;
  406. }
  407. token ~= chr;
  408. bool tokenFound = true;
  409. //pragma(msg,BuildArgumentParser!(list));
  410. mixin(BuildArgumentParser!(list));
  411. if (tokenFound) {
  412. // interpretate and return the arguments
  413. token = "";
  414. }
  415. }
  416. }
  417. }
  418. }
  419. }