Commit 5e811526dad2ef339649fec64e147d17314cc0be
1 parent
85eedc50
Added updated lapp.nim
Showing
1 changed file
with
98 additions
and
43 deletions
lapp.nim
1 | 1 | import strutils |
2 | -from os import paramCount, paramStr | |
2 | +import os | |
3 | 3 | import tables |
4 | 4 | export tables.`[]` |
5 | 5 | |
... | ... | @@ -50,10 +50,10 @@ proc get(L: PLexer; t: var TLexType): string = |
50 | 50 | next(L) |
51 | 51 | if thisChar(L) == '.': |
52 | 52 | t = tfloat |
53 | - result.add(c) | |
53 | + result.add(thisChar(L)) | |
54 | 54 | next(L) |
55 | 55 | while thisChar(L) in Digits: |
56 | - result.add(c) | |
56 | + result.add(thisChar(L)) | |
57 | 57 | next(L) |
58 | 58 | of '.': # ".", "..", "..." |
59 | 59 | if thisChar(L) == '.': |
... | ... | @@ -89,17 +89,17 @@ type |
89 | 89 | vFile, |
90 | 90 | vSeq |
91 | 91 | |
92 | - PValue = ref TValue | |
93 | - TValue = object | |
94 | - case kind: TValueKind | |
95 | - of vInt: asInt *: int | |
96 | - of vFloat: asFloat *: float | |
97 | - of vString: asString *: string | |
98 | - of vBool: asBool *: bool | |
92 | + PValue* = ref TValue | |
93 | + TValue* = object | |
94 | + case kind*: TValueKind | |
95 | + of vInt: asInt*: int | |
96 | + of vFloat: asFloat*: float | |
97 | + of vString: asString*: string | |
98 | + of vBool: asBool*: bool | |
99 | 99 | of vFile: |
100 | - asFile *: File | |
101 | - fileName *: string | |
102 | - of vSeq: asSeq *: seq[PValue] | |
100 | + asFile*: File | |
101 | + fileName*: string | |
102 | + of vSeq: asSeq*: seq[PValue] | |
103 | 103 | |
104 | 104 | proc boolValue(c: bool): PValue = PValue(kind: vBool, asBool: c) |
105 | 105 | |
... | ... | @@ -125,7 +125,7 @@ var |
125 | 125 | progname, usage: string |
126 | 126 | aliases: array[char,string] |
127 | 127 | parm_spec = initTable[string,PSpec]() |
128 | - | |
128 | + | |
129 | 129 | proc fail(msg: string) = |
130 | 130 | stderr.writeln(progname & ": " & msg) |
131 | 131 | quit(usage) |
... | ... | @@ -205,6 +205,7 @@ proc parseSpec(u: string) = |
205 | 205 | defValue = "false" |
206 | 206 | |
207 | 207 | if name != nil: |
208 | + # echo("Param: " & name & " type: " & $ftype & " needsvalue: " & $(ftype != "bool") & " default: " & $defValue & " multiple: " & $multiple) | |
208 | 209 | let spec = PSpec(defVal:defValue, ptype: ftype, needsValue: ftype != "bool",multiple:multiple) |
209 | 210 | aliases[alias] = name |
210 | 211 | parm_spec[name] = spec |
... | ... | @@ -212,14 +213,13 @@ proc parseSpec(u: string) = |
212 | 213 | proc tail(s: string): string = s[1..(-1)] |
213 | 214 | |
214 | 215 | var |
215 | - files: array[1..MAX_FILES,File] | |
216 | - nfiles = 0 | |
216 | + files = newSeq[File]() | |
217 | 217 | |
218 | 218 | proc closeFiles() {.noconv.} = |
219 | - if nfiles == 0: return | |
220 | - for i in 1..nfiles: files[i].close() | |
219 | + for f in files: | |
220 | + f.close() | |
221 | 221 | |
222 | -proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | |
222 | +proc parseArguments*(usage: string, args: seq[string]): Table[string,PValue] = | |
223 | 223 | var |
224 | 224 | vars = initTable[string,PValue]() |
225 | 225 | n = len(args) - 1 |
... | ... | @@ -253,6 +253,9 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = |
253 | 253 | parseSpec(usage) |
254 | 254 | addQuitProc(closeFiles) |
255 | 255 | |
256 | + # Collect failures | |
257 | + var failures = newSeq[string]() | |
258 | + | |
256 | 259 | # parse the flags and arguments |
257 | 260 | while i <= n: |
258 | 261 | arg = next() |
... | ... | @@ -275,7 +278,8 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = |
275 | 278 | for c in arg.tail: |
276 | 279 | let f = get_alias(c) |
277 | 280 | let i = get_spec(f) |
278 | - if i.needsValue: fail("needs value! " & f) | |
281 | + if i.needsValue: | |
282 | + failures.add("option " & f & " needs a value") | |
279 | 283 | flagvalues.add(@[f,"true"]) |
280 | 284 | i.used = true |
281 | 285 | else: # argument (stored as \001, \002, etc |
... | ... | @@ -286,14 +290,28 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = |
286 | 290 | if not info.multiple: k += 1 |
287 | 291 | flagvalues.add(@[flag,value]) |
288 | 292 | info.used = true |
289 | - | |
293 | + | |
294 | + # Some options disables checking | |
295 | + var enableChecks = true | |
296 | + for flag,info in parm_spec: | |
297 | + if info.used: | |
298 | + if flag == "help" or flag == "version": | |
299 | + enableChecks = false | |
300 | + | |
301 | + | |
290 | 302 | # any flags not mentioned? |
291 | 303 | for flag,info in parm_spec: |
292 | 304 | if not info.used: |
293 | 305 | if info.defVal == "": # no default! |
294 | - fail("required option or argument missing: " & flag) | |
295 | - flagvalues.add(@[flag,info.defVal]) | |
296 | - | |
306 | + failures.add("required option or argument missing: " & flag) | |
307 | + else: | |
308 | + flagvalues.add(@[flag,info.defVal]) | |
309 | + | |
310 | + if enableChecks: | |
311 | + # any failures up until now - then we fail | |
312 | + if failures.len > 0: | |
313 | + fail(failures.join("\n") & "\n") | |
314 | + | |
297 | 315 | # cool, we have the info, can convert known flags |
298 | 316 | for item in flagvalues: |
299 | 317 | var pval: PValue; |
... | ... | @@ -307,14 +325,14 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = |
307 | 325 | try: |
308 | 326 | v = value.parseInt |
309 | 327 | except: |
310 | - fail("bad integer") | |
328 | + fail("bad integer for " & flag) | |
311 | 329 | pval = intValue(v) |
312 | 330 | of "float": |
313 | 331 | var v: float |
314 | 332 | try: |
315 | 333 | v = value.parseFloat |
316 | 334 | except: |
317 | - fail("bad integer") | |
335 | + fail("bad float for " & flag) | |
318 | 336 | pval = floatValue(v) |
319 | 337 | of "bool": |
320 | 338 | pval = boolValue(value.parseBool) |
... | ... | @@ -324,12 +342,17 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = |
324 | 342 | var f: File |
325 | 343 | try: |
326 | 344 | if info.ptype == "infile": |
327 | - f = if value=="stdin": stdin else: open(value,fmRead) | |
345 | + if value == "stdin": | |
346 | + f = stdin | |
347 | + else: | |
348 | + f = open(value, fmRead) | |
328 | 349 | else: |
329 | - f = if value=="stdout": stdout else: open(value,fmWrite) | |
350 | + if value == "stdout": | |
351 | + f = stdout | |
352 | + else: | |
353 | + f = open(value, fmWrite) | |
330 | 354 | # they will be closed automatically on program exit |
331 | - nfiles += 1 | |
332 | - if nfiles <= MAX_FILES: files[nfiles] = f | |
355 | + files.add(f) | |
333 | 356 | except: |
334 | 357 | fail("cannot open " & value) |
335 | 358 | pval = fileValue(f,value) |
... | ... | @@ -357,23 +380,55 @@ proc parse*(usage: string): Table[string,PValue] = |
357 | 380 | args[i] = paramStr(i) |
358 | 381 | return parseArguments(usage,args) |
359 | 382 | |
383 | +# Helper proc for verbosity level. | |
384 | +proc verbosityLevel*(args: Table[string,PValue]): int = | |
385 | + if args.hasKey("verbose"): | |
386 | + let verbosity = args["verbose"].asSeq | |
387 | + result = verbosity.len | |
388 | + if not verbosity[0].asBool: | |
389 | + result = 0 | |
390 | + else: | |
391 | + result = 0 | |
392 | + | |
393 | +# Helper to check if we should show version | |
394 | +proc showVersion*(args: Table[string,PValue]): bool = | |
395 | + args["version"].asBool | |
396 | + | |
397 | +# Helper to check if we should show help | |
398 | +proc showHelp*(args: Table[string,PValue]): bool = | |
399 | + args["help"].asBool | |
400 | + | |
401 | + | |
402 | +# Typical usage | |
360 | 403 | when isMainModule: |
361 | - var args = parse""" | |
362 | - head [flags] filename | |
363 | - -n: (default 10) number of lines | |
364 | - -v,--verbose: (bool...) verbosity level | |
365 | - -a,--alpha useless parm | |
366 | - <file>: (default stdin...) | |
367 | - |<out>: (default stdout) | |
404 | + let help = """ | |
405 | + head [flags] files... [out] | |
406 | + -h,--help Show this help | |
407 | + --version Show version | |
408 | + -n: (default 10) Number of lines to show | |
409 | + -v,--verbose: (bool...) Verbosity level, ignored | |
410 | + -o,--out: (default stdout) Optional outfile, defaults to stdout | |
411 | + <files>: (default stdin...) Files to take head of | |
368 | 412 | """ |
413 | + # On parsing failure this will show usage automatically | |
414 | + var args = parse(help) | |
369 | 415 | |
370 | - echo args["n"].asInt | |
371 | - echo args["alpha"].asBool | |
416 | + # These two are special, they short out | |
417 | + if args.showHelp: quit(help) | |
418 | + if args.showVersion: quit("Version: 1.99") | |
419 | + | |
420 | + # Ok, so what did we get... | |
421 | + let n = args["n"].asInt | |
422 | + | |
423 | + # This one is a helper | |
424 | + let v = verbosityLevel(args) | |
372 | 425 | |
373 | - for v in args["verbose"].asSeq: | |
374 | - echo "got ",v.asBool | |
426 | + echo "Lines to show: " & $n | |
427 | + echo "Verbosity level: " & $verbosityLevel(args) | |
375 | 428 | |
376 | 429 | let myfiles = args["files"].asSeq |
430 | + var outFile = args["out"].asFile | |
431 | + | |
377 | 432 | for f in myfiles: |
378 | - echo f.asFile.readLine() | |
379 | - | |
433 | + for i in 1..n: | |
434 | + writeln(outFile, string(f.asFile.readLine())) | ... | ... |