Commit 5e811526dad2ef339649fec64e147d17314cc0be
1 parent
85eedc50
Added updated lapp.nim
Showing
1 changed file
with
98 additions
and
43 deletions
lapp.nim
1 | import strutils | 1 | import strutils |
2 | -from os import paramCount, paramStr | 2 | +import os |
3 | import tables | 3 | import tables |
4 | export tables.`[]` | 4 | export tables.`[]` |
5 | 5 | ||
@@ -50,10 +50,10 @@ proc get(L: PLexer; t: var TLexType): string = | @@ -50,10 +50,10 @@ proc get(L: PLexer; t: var TLexType): string = | ||
50 | next(L) | 50 | next(L) |
51 | if thisChar(L) == '.': | 51 | if thisChar(L) == '.': |
52 | t = tfloat | 52 | t = tfloat |
53 | - result.add(c) | 53 | + result.add(thisChar(L)) |
54 | next(L) | 54 | next(L) |
55 | while thisChar(L) in Digits: | 55 | while thisChar(L) in Digits: |
56 | - result.add(c) | 56 | + result.add(thisChar(L)) |
57 | next(L) | 57 | next(L) |
58 | of '.': # ".", "..", "..." | 58 | of '.': # ".", "..", "..." |
59 | if thisChar(L) == '.': | 59 | if thisChar(L) == '.': |
@@ -89,17 +89,17 @@ type | @@ -89,17 +89,17 @@ type | ||
89 | vFile, | 89 | vFile, |
90 | vSeq | 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 | of vFile: | 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 | proc boolValue(c: bool): PValue = PValue(kind: vBool, asBool: c) | 104 | proc boolValue(c: bool): PValue = PValue(kind: vBool, asBool: c) |
105 | 105 | ||
@@ -125,7 +125,7 @@ var | @@ -125,7 +125,7 @@ var | ||
125 | progname, usage: string | 125 | progname, usage: string |
126 | aliases: array[char,string] | 126 | aliases: array[char,string] |
127 | parm_spec = initTable[string,PSpec]() | 127 | parm_spec = initTable[string,PSpec]() |
128 | - | 128 | + |
129 | proc fail(msg: string) = | 129 | proc fail(msg: string) = |
130 | stderr.writeln(progname & ": " & msg) | 130 | stderr.writeln(progname & ": " & msg) |
131 | quit(usage) | 131 | quit(usage) |
@@ -205,6 +205,7 @@ proc parseSpec(u: string) = | @@ -205,6 +205,7 @@ proc parseSpec(u: string) = | ||
205 | defValue = "false" | 205 | defValue = "false" |
206 | 206 | ||
207 | if name != nil: | 207 | if name != nil: |
208 | + # echo("Param: " & name & " type: " & $ftype & " needsvalue: " & $(ftype != "bool") & " default: " & $defValue & " multiple: " & $multiple) | ||
208 | let spec = PSpec(defVal:defValue, ptype: ftype, needsValue: ftype != "bool",multiple:multiple) | 209 | let spec = PSpec(defVal:defValue, ptype: ftype, needsValue: ftype != "bool",multiple:multiple) |
209 | aliases[alias] = name | 210 | aliases[alias] = name |
210 | parm_spec[name] = spec | 211 | parm_spec[name] = spec |
@@ -212,14 +213,13 @@ proc parseSpec(u: string) = | @@ -212,14 +213,13 @@ proc parseSpec(u: string) = | ||
212 | proc tail(s: string): string = s[1..(-1)] | 213 | proc tail(s: string): string = s[1..(-1)] |
213 | 214 | ||
214 | var | 215 | var |
215 | - files: array[1..MAX_FILES,File] | ||
216 | - nfiles = 0 | 216 | + files = newSeq[File]() |
217 | 217 | ||
218 | proc closeFiles() {.noconv.} = | 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 | var | 223 | var |
224 | vars = initTable[string,PValue]() | 224 | vars = initTable[string,PValue]() |
225 | n = len(args) - 1 | 225 | n = len(args) - 1 |
@@ -253,6 +253,9 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -253,6 +253,9 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
253 | parseSpec(usage) | 253 | parseSpec(usage) |
254 | addQuitProc(closeFiles) | 254 | addQuitProc(closeFiles) |
255 | 255 | ||
256 | + # Collect failures | ||
257 | + var failures = newSeq[string]() | ||
258 | + | ||
256 | # parse the flags and arguments | 259 | # parse the flags and arguments |
257 | while i <= n: | 260 | while i <= n: |
258 | arg = next() | 261 | arg = next() |
@@ -275,7 +278,8 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -275,7 +278,8 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
275 | for c in arg.tail: | 278 | for c in arg.tail: |
276 | let f = get_alias(c) | 279 | let f = get_alias(c) |
277 | let i = get_spec(f) | 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 | flagvalues.add(@[f,"true"]) | 283 | flagvalues.add(@[f,"true"]) |
280 | i.used = true | 284 | i.used = true |
281 | else: # argument (stored as \001, \002, etc | 285 | else: # argument (stored as \001, \002, etc |
@@ -286,14 +290,28 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -286,14 +290,28 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
286 | if not info.multiple: k += 1 | 290 | if not info.multiple: k += 1 |
287 | flagvalues.add(@[flag,value]) | 291 | flagvalues.add(@[flag,value]) |
288 | info.used = true | 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 | # any flags not mentioned? | 302 | # any flags not mentioned? |
291 | for flag,info in parm_spec: | 303 | for flag,info in parm_spec: |
292 | if not info.used: | 304 | if not info.used: |
293 | if info.defVal == "": # no default! | 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 | # cool, we have the info, can convert known flags | 315 | # cool, we have the info, can convert known flags |
298 | for item in flagvalues: | 316 | for item in flagvalues: |
299 | var pval: PValue; | 317 | var pval: PValue; |
@@ -307,14 +325,14 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -307,14 +325,14 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
307 | try: | 325 | try: |
308 | v = value.parseInt | 326 | v = value.parseInt |
309 | except: | 327 | except: |
310 | - fail("bad integer") | 328 | + fail("bad integer for " & flag) |
311 | pval = intValue(v) | 329 | pval = intValue(v) |
312 | of "float": | 330 | of "float": |
313 | var v: float | 331 | var v: float |
314 | try: | 332 | try: |
315 | v = value.parseFloat | 333 | v = value.parseFloat |
316 | except: | 334 | except: |
317 | - fail("bad integer") | 335 | + fail("bad float for " & flag) |
318 | pval = floatValue(v) | 336 | pval = floatValue(v) |
319 | of "bool": | 337 | of "bool": |
320 | pval = boolValue(value.parseBool) | 338 | pval = boolValue(value.parseBool) |
@@ -324,12 +342,17 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | @@ -324,12 +342,17 @@ proc parseArguments(usage: string, args: seq[string]): Table[string,PValue] = | ||
324 | var f: File | 342 | var f: File |
325 | try: | 343 | try: |
326 | if info.ptype == "infile": | 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 | else: | 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 | # they will be closed automatically on program exit | 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 | except: | 356 | except: |
334 | fail("cannot open " & value) | 357 | fail("cannot open " & value) |
335 | pval = fileValue(f,value) | 358 | pval = fileValue(f,value) |
@@ -357,23 +380,55 @@ proc parse*(usage: string): Table[string,PValue] = | @@ -357,23 +380,55 @@ proc parse*(usage: string): Table[string,PValue] = | ||
357 | args[i] = paramStr(i) | 380 | args[i] = paramStr(i) |
358 | return parseArguments(usage,args) | 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 | when isMainModule: | 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 | let myfiles = args["files"].asSeq | 429 | let myfiles = args["files"].asSeq |
430 | + var outFile = args["out"].asFile | ||
431 | + | ||
377 | for f in myfiles: | 432 | for f in myfiles: |
378 | - echo f.asFile.readLine() | ||
379 | - | 433 | + for i in 1..n: |
434 | + writeln(outFile, string(f.asFile.readLine())) |